- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / api / location / location_manager.cc
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/extensions/api/location/location_manager.h"
6
7 #include <math.h>
8 #include <vector>
9
10 #include "base/bind.h"
11 #include "base/lazy_instance.h"
12 #include "base/time/time.h"
13 #include "chrome/browser/chrome_notification_types.h"
14 #include "chrome/browser/extensions/event_router.h"
15 #include "chrome/browser/extensions/extension_system.h"
16 #include "chrome/common/extensions/api/location.h"
17 #include "chrome/common/extensions/extension.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "content/public/browser/geolocation_provider.h"
20 #include "content/public/browser/notification_details.h"
21 #include "content/public/browser/notification_source.h"
22 #include "content/public/common/geoposition.h"
23 #include "extensions/common/permissions/permission_set.h"
24
25 using content::BrowserThread;
26
27 // TODO(vadimt): Add tests.
28 namespace extensions {
29
30 namespace location = api::location;
31
32 namespace updatepolicy {
33
34 // Base class for all update policies for sending a location.
35 class UpdatePolicy : public base::RefCounted<UpdatePolicy> {
36  public:
37   explicit UpdatePolicy() {}
38
39   // True if the caller should send an update based off of this policy.
40   virtual bool ShouldSendUpdate(const content::Geoposition&) const = 0;
41
42   // Updates any policy state on reporting a position.
43   virtual void OnPositionReported(const content::Geoposition&) = 0;
44
45  protected:
46   virtual ~UpdatePolicy() {}
47
48  private:
49   friend class base::RefCounted<UpdatePolicy>;
50   DISALLOW_COPY_AND_ASSIGN(UpdatePolicy);
51 };
52
53 // A policy that controls sending an update below a distance threshold.
54 class DistanceBasedUpdatePolicy : public UpdatePolicy {
55  public:
56   explicit DistanceBasedUpdatePolicy(double distance_update_threshold_meters) :
57       distance_update_threshold_meters_(distance_update_threshold_meters)
58   {}
59
60   // UpdatePolicy Implementation
61   virtual bool ShouldSendUpdate(const content::Geoposition& position) const
62       OVERRIDE {
63     return !last_updated_position_.Validate() ||
64         Distance(position.latitude,
65                  position.longitude,
66                  last_updated_position_.latitude,
67                  last_updated_position_.longitude) >
68             distance_update_threshold_meters_;
69   }
70
71   virtual void OnPositionReported(const content::Geoposition& position)
72       OVERRIDE {
73     last_updated_position_ = position;
74   }
75
76  private:
77   virtual ~DistanceBasedUpdatePolicy() {}
78
79   // Calculates the distance between two latitude and longitude points.
80   static double Distance(const double latitude1,
81                          const double longitude1,
82                          const double latitude2,
83                          const double longitude2) {
84     // The earth has a radius of about 6371 km.
85     const double kRadius = 6371000;
86     const double kPi = 3.14159265358979323846;
87     const double kDegreesToRadians = kPi / 180.0;
88
89     // Conversions
90     const double latitude1Rad = latitude1 * kDegreesToRadians;
91     const double latitude2Rad = latitude2 * kDegreesToRadians;
92     const double latitudeDistRad = latitude2Rad - latitude1Rad;
93     const double longitudeDistRad = (longitude2 - longitude1) *
94                                     kDegreesToRadians;
95
96     // The Haversine Formula determines the great circle distance
97     // between two points on a sphere.
98     const double chordLengthSquared = pow(sin(latitudeDistRad / 2.0), 2) +
99                                       (pow(sin(longitudeDistRad / 2.0), 2) *
100                                        cos(latitude1Rad) *
101                                        cos(latitude2Rad));
102     const double angularDistance = 2.0 * atan2(sqrt(chordLengthSquared),
103                                                sqrt(1.0 - chordLengthSquared));
104     return kRadius * angularDistance;
105   }
106
107   const double distance_update_threshold_meters_;
108   content::Geoposition last_updated_position_;
109
110   DISALLOW_COPY_AND_ASSIGN(DistanceBasedUpdatePolicy);
111 };
112
113 // A policy that controls sending an update above a time threshold.
114 class TimeBasedUpdatePolicy : public UpdatePolicy {
115  public:
116   explicit TimeBasedUpdatePolicy(double time_between_updates_ms) :
117       time_between_updates_ms_(time_between_updates_ms)
118   {}
119
120   // UpdatePolicy Implementation
121   virtual bool ShouldSendUpdate(const content::Geoposition&) const OVERRIDE {
122     return (base::Time::Now() - last_update_time_).InMilliseconds() >
123         time_between_updates_ms_;
124   }
125
126   virtual void OnPositionReported(const content::Geoposition&) OVERRIDE {
127     last_update_time_ = base::Time::Now();
128   }
129
130  private:
131   virtual ~TimeBasedUpdatePolicy() {}
132
133   base::Time last_update_time_;
134   const double time_between_updates_ms_;
135
136   DISALLOW_COPY_AND_ASSIGN(TimeBasedUpdatePolicy);
137 };
138
139 } // namespace updatepolicy
140
141 // Request created by chrome.location.watchLocation() call.
142 // Lives in the IO thread, except for the constructor.
143 class LocationRequest
144     : public base::RefCountedThreadSafe<LocationRequest,
145                                         BrowserThread::DeleteOnIOThread> {
146  public:
147   LocationRequest(
148       const base::WeakPtr<LocationManager>& location_manager,
149       const std::string& extension_id,
150       const std::string& request_name,
151       const double* distance_update_threshold_meters,
152       const double* time_between_updates_ms);
153
154   // Finishes the necessary setup for this object.
155   // Call this method immediately after taking a strong reference
156   // to this object.
157   //
158   // Ideally, we would do this at construction time, but currently
159   // our refcount starts at zero. BrowserThread::PostTask will take a ref
160   // and potentially release it before we are done, destroying us in the
161   // constructor.
162   void Initialize();
163
164   const std::string& request_name() const { return request_name_; }
165
166   // Grants permission for using geolocation.
167   static void GrantPermission();
168
169  private:
170   friend class base::DeleteHelper<LocationRequest>;
171   friend struct BrowserThread::DeleteOnThread<BrowserThread::IO>;
172
173   virtual ~LocationRequest();
174
175   void AddObserverOnIOThread();
176
177   void OnLocationUpdate(const content::Geoposition& position);
178
179   // Determines if all policies say to send a position update.
180   // If there are no policies, this always says yes.
181   bool ShouldSendUpdate(const content::Geoposition& position);
182
183   // Updates the policies on sending a position update.
184   void OnPositionReported(const content::Geoposition& position);
185
186   // Request name.
187   const std::string request_name_;
188
189   // Id of the owner extension.
190   const std::string extension_id_;
191
192   // Owning location manager.
193   const base::WeakPtr<LocationManager> location_manager_;
194
195   // Holds Update Policies.
196   typedef std::vector<scoped_refptr<updatepolicy::UpdatePolicy> >
197       UpdatePolicyVector;
198   UpdatePolicyVector update_policies_;
199
200   content::GeolocationProvider::LocationUpdateCallback callback_;
201
202   DISALLOW_COPY_AND_ASSIGN(LocationRequest);
203 };
204
205 LocationRequest::LocationRequest(
206     const base::WeakPtr<LocationManager>& location_manager,
207     const std::string& extension_id,
208     const std::string& request_name,
209     const double* distance_update_threshold_meters,
210     const double* time_between_updates_ms)
211     : request_name_(request_name),
212       extension_id_(extension_id),
213       location_manager_(location_manager) {
214   // TODO(vadimt): use request_info.
215   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
216
217   if (time_between_updates_ms) {
218     update_policies_.push_back(
219         new updatepolicy::TimeBasedUpdatePolicy(
220             *time_between_updates_ms));
221   }
222
223   if (distance_update_threshold_meters) {
224     update_policies_.push_back(
225         new updatepolicy::DistanceBasedUpdatePolicy(
226               *distance_update_threshold_meters));
227   }
228 }
229
230 void LocationRequest::Initialize() {
231   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
232   callback_ = base::Bind(&LocationRequest::OnLocationUpdate,
233                          base::Unretained(this));
234
235   BrowserThread::PostTask(
236       BrowserThread::IO,
237       FROM_HERE,
238       base::Bind(&LocationRequest::AddObserverOnIOThread,
239                  this));
240 }
241
242 void LocationRequest::GrantPermission() {
243   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
244   content::GeolocationProvider::GetInstance()->UserDidOptIntoLocationServices();
245 }
246
247 LocationRequest::~LocationRequest() {
248   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
249   content::GeolocationProvider::GetInstance()->RemoveLocationUpdateCallback(
250       callback_);
251 }
252
253 void LocationRequest::AddObserverOnIOThread() {
254   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
255
256   // TODO(vadimt): This can get a location cached by GeolocationProvider,
257   // contrary to the API definition which says that creating a location watch
258   // will get new location.
259   content::GeolocationProvider::GetInstance()->AddLocationUpdateCallback(
260       callback_, true);
261 }
262
263 void LocationRequest::OnLocationUpdate(const content::Geoposition& position) {
264   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
265   if (ShouldSendUpdate(position)) {
266     OnPositionReported(position);
267     BrowserThread::PostTask(
268         BrowserThread::UI,
269         FROM_HERE,
270         base::Bind(&LocationManager::SendLocationUpdate,
271                    location_manager_,
272                    extension_id_,
273                    request_name_,
274                    position));
275   }
276 }
277
278 bool LocationRequest::ShouldSendUpdate(const content::Geoposition& position) {
279   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
280   for (UpdatePolicyVector::iterator it = update_policies_.begin();
281        it != update_policies_.end();
282        ++it) {
283     if (!((*it)->ShouldSendUpdate(position))) {
284       return false;
285     }
286   }
287   return true;
288 }
289
290 void LocationRequest::OnPositionReported(const content::Geoposition& position) {
291   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
292   for (UpdatePolicyVector::iterator it = update_policies_.begin();
293        it != update_policies_.end();
294        ++it) {
295     (*it)->OnPositionReported(position);
296   }
297 }
298
299 LocationManager::LocationManager(Profile* profile)
300     : profile_(profile) {
301   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
302                  content::Source<Profile>(profile_));
303   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
304                  content::Source<Profile>(profile_));
305 }
306
307 void LocationManager::AddLocationRequest(
308     const std::string& extension_id,
309     const std::string& request_name,
310     const double* distance_update_threshold_meters,
311     const double* time_between_updates_ms) {
312   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
313   // TODO(vadimt): Consider resuming requests after restarting the browser.
314
315   // Override any old request with the same name.
316   RemoveLocationRequest(extension_id, request_name);
317
318   LocationRequestPointer location_request =
319       new LocationRequest(AsWeakPtr(),
320                           extension_id,
321                           request_name,
322                           distance_update_threshold_meters,
323                           time_between_updates_ms);
324   location_request->Initialize();
325   location_requests_.insert(
326       LocationRequestMap::value_type(extension_id, location_request));
327 }
328
329 void LocationManager::RemoveLocationRequest(const std::string& extension_id,
330                                             const std::string& name) {
331   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
332
333   std::pair<LocationRequestMap::iterator, LocationRequestMap::iterator>
334       extension_range = location_requests_.equal_range(extension_id);
335
336   for (LocationRequestMap::iterator it = extension_range.first;
337        it != extension_range.second;
338        ++it) {
339     if (it->second->request_name() == name) {
340       location_requests_.erase(it);
341       return;
342     }
343   }
344 }
345
346 LocationManager::~LocationManager() {
347 }
348
349 void LocationManager::GeopositionToApiCoordinates(
350       const content::Geoposition& position,
351       location::Coordinates* coordinates) {
352   coordinates->latitude = position.latitude;
353   coordinates->longitude = position.longitude;
354   if (position.altitude > -10000.)
355     coordinates->altitude.reset(new double(position.altitude));
356   coordinates->accuracy = position.accuracy;
357   if (position.altitude_accuracy >= 0.) {
358     coordinates->altitude_accuracy.reset(
359         new double(position.altitude_accuracy));
360   }
361   if (position.heading >= 0. && position.heading <= 360.)
362     coordinates->heading.reset(new double(position.heading));
363   if (position.speed >= 0.)
364     coordinates->speed.reset(new double(position.speed));
365 }
366
367 void LocationManager::SendLocationUpdate(
368     const std::string& extension_id,
369     const std::string& request_name,
370     const content::Geoposition& position) {
371   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
372
373   scoped_ptr<base::ListValue> args(new base::ListValue());
374   std::string event_name;
375
376   if (position.Validate() &&
377       position.error_code == content::Geoposition::ERROR_CODE_NONE) {
378     // Set data for onLocationUpdate event.
379     location::Location location;
380     location.name = request_name;
381     GeopositionToApiCoordinates(position, &location.coords);
382     location.timestamp = position.timestamp.ToJsTime();
383
384     args->Append(location.ToValue().release());
385     event_name = location::OnLocationUpdate::kEventName;
386   } else {
387     // Set data for onLocationError event.
388     // TODO(vadimt): Set name.
389     args->AppendString(position.error_message);
390     event_name = location::OnLocationError::kEventName;
391   }
392
393   scoped_ptr<Event> event(new Event(event_name, args.Pass()));
394
395   ExtensionSystem::Get(profile_)->event_router()->
396       DispatchEventToExtension(extension_id, event.Pass());
397 }
398
399 void LocationManager::Observe(int type,
400                               const content::NotificationSource& source,
401                               const content::NotificationDetails& details) {
402   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
403
404   switch (type) {
405     case chrome::NOTIFICATION_EXTENSION_LOADED: {
406       // Grants permission to use geolocation once an extension with "location"
407       // permission is loaded.
408       const Extension* extension =
409           content::Details<const Extension>(details).ptr();
410
411       if (extension->HasAPIPermission(APIPermission::kLocation)) {
412           BrowserThread::PostTask(
413               BrowserThread::IO,
414               FROM_HERE,
415               base::Bind(&LocationRequest::GrantPermission));
416       }
417       break;
418     }
419     case chrome::NOTIFICATION_EXTENSION_UNLOADED: {
420       // Delete all requests from the unloaded extension.
421       const Extension* extension =
422           content::Details<const UnloadedExtensionInfo>(details)->extension;
423       location_requests_.erase(extension->id());
424       break;
425     }
426     default:
427       NOTREACHED();
428       break;
429   }
430 }
431
432 static base::LazyInstance<ProfileKeyedAPIFactory<LocationManager> >
433 g_factory = LAZY_INSTANCE_INITIALIZER;
434
435 // static
436 ProfileKeyedAPIFactory<LocationManager>* LocationManager::GetFactoryInstance() {
437   return &g_factory.Get();
438 }
439
440  // static
441 LocationManager* LocationManager::Get(Profile* profile) {
442   return ProfileKeyedAPIFactory<LocationManager>::GetForProfile(profile);
443 }
444
445 }  // namespace extensions