• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/common/extensions/api/location.h"
14 #include "content/public/browser/browser_thread.h"
15 #include "content/public/browser/geolocation_provider.h"
16 #include "content/public/common/geoposition.h"
17 #include "extensions/browser/event_router.h"
18 #include "extensions/browser/extension_registry.h"
19 #include "extensions/browser/extension_system.h"
20 #include "extensions/common/extension.h"
21 #include "extensions/common/permissions/permission_set.h"
22 #include "extensions/common/permissions/permissions_data.h"
23 
24 using content::BrowserThread;
25 
26 // TODO(vadimt): Add tests.
27 namespace extensions {
28 
29 namespace location = api::location;
30 
31 namespace updatepolicy {
32 
33 // Base class for all update policies for sending a location.
34 class UpdatePolicy : public base::RefCounted<UpdatePolicy> {
35  public:
UpdatePolicy()36   explicit UpdatePolicy() {}
37 
38   // True if the caller should send an update based off of this policy.
39   virtual bool ShouldSendUpdate(const content::Geoposition&) const = 0;
40 
41   // Updates any policy state on reporting a position.
42   virtual void OnPositionReported(const content::Geoposition&) = 0;
43 
44  protected:
~UpdatePolicy()45   virtual ~UpdatePolicy() {}
46 
47  private:
48   friend class base::RefCounted<UpdatePolicy>;
49   DISALLOW_COPY_AND_ASSIGN(UpdatePolicy);
50 };
51 
52 // A policy that controls sending an update below a distance threshold.
53 class DistanceBasedUpdatePolicy : public UpdatePolicy {
54  public:
DistanceBasedUpdatePolicy(double distance_update_threshold_meters)55   explicit DistanceBasedUpdatePolicy(double distance_update_threshold_meters) :
56       distance_update_threshold_meters_(distance_update_threshold_meters)
57   {}
58 
59   // UpdatePolicy Implementation
ShouldSendUpdate(const content::Geoposition & position) const60   virtual bool ShouldSendUpdate(const content::Geoposition& position) const
61       OVERRIDE {
62     return !last_updated_position_.Validate() ||
63         Distance(position.latitude,
64                  position.longitude,
65                  last_updated_position_.latitude,
66                  last_updated_position_.longitude) >
67             distance_update_threshold_meters_;
68   }
69 
OnPositionReported(const content::Geoposition & position)70   virtual void OnPositionReported(const content::Geoposition& position)
71       OVERRIDE {
72     last_updated_position_ = position;
73   }
74 
75  private:
~DistanceBasedUpdatePolicy()76   virtual ~DistanceBasedUpdatePolicy() {}
77 
78   // Calculates the distance between two latitude and longitude points.
Distance(const double latitude1,const double longitude1,const double latitude2,const double longitude2)79   static double Distance(const double latitude1,
80                          const double longitude1,
81                          const double latitude2,
82                          const double longitude2) {
83     // The earth has a radius of about 6371 km.
84     const double kRadius = 6371000;
85     const double kPi = 3.14159265358979323846;
86     const double kDegreesToRadians = kPi / 180.0;
87 
88     // Conversions
89     const double latitude1Rad = latitude1 * kDegreesToRadians;
90     const double latitude2Rad = latitude2 * kDegreesToRadians;
91     const double latitudeDistRad = latitude2Rad - latitude1Rad;
92     const double longitudeDistRad = (longitude2 - longitude1) *
93                                     kDegreesToRadians;
94 
95     // The Haversine Formula determines the great circle distance
96     // between two points on a sphere.
97     const double chordLengthSquared = pow(sin(latitudeDistRad / 2.0), 2) +
98                                       (pow(sin(longitudeDistRad / 2.0), 2) *
99                                        cos(latitude1Rad) *
100                                        cos(latitude2Rad));
101     const double angularDistance = 2.0 * atan2(sqrt(chordLengthSquared),
102                                                sqrt(1.0 - chordLengthSquared));
103     return kRadius * angularDistance;
104   }
105 
106   const double distance_update_threshold_meters_;
107   content::Geoposition last_updated_position_;
108 
109   DISALLOW_COPY_AND_ASSIGN(DistanceBasedUpdatePolicy);
110 };
111 
112 // A policy that controls sending an update above a time threshold.
113 class TimeBasedUpdatePolicy : public UpdatePolicy {
114  public:
TimeBasedUpdatePolicy(double time_between_updates_ms)115   explicit TimeBasedUpdatePolicy(double time_between_updates_ms) :
116       time_between_updates_ms_(time_between_updates_ms)
117   {}
118 
119   // UpdatePolicy Implementation
ShouldSendUpdate(const content::Geoposition &) const120   virtual bool ShouldSendUpdate(const content::Geoposition&) const OVERRIDE {
121     return (base::Time::Now() - last_update_time_).InMilliseconds() >
122         time_between_updates_ms_;
123   }
124 
OnPositionReported(const content::Geoposition &)125   virtual void OnPositionReported(const content::Geoposition&) OVERRIDE {
126     last_update_time_ = base::Time::Now();
127   }
128 
129  private:
~TimeBasedUpdatePolicy()130   virtual ~TimeBasedUpdatePolicy() {}
131 
132   base::Time last_update_time_;
133   const double time_between_updates_ms_;
134 
135   DISALLOW_COPY_AND_ASSIGN(TimeBasedUpdatePolicy);
136 };
137 
138 } // namespace updatepolicy
139 
140 // Request created by chrome.location.watchLocation() call.
141 class LocationRequest : public base::RefCounted<LocationRequest> {
142  public:
143   LocationRequest(
144       LocationManager* location_manager,
145       const std::string& extension_id,
146       const std::string& request_name,
147       const double* distance_update_threshold_meters,
148       const double* time_between_updates_ms);
149 
request_name() const150   const std::string& request_name() const { return request_name_; }
151 
152  private:
153   friend class base::RefCounted<LocationRequest>;
154    ~LocationRequest();
155 
156   void OnLocationUpdate(const content::Geoposition& position);
157 
158   // Determines if all policies say to send a position update.
159   // If there are no policies, this always says yes.
160   bool ShouldSendUpdate(const content::Geoposition& position);
161 
162   // Updates the policies on sending a position update.
163   void OnPositionReported(const content::Geoposition& position);
164 
165   // Request name.
166   const std::string request_name_;
167 
168   // Id of the owner extension.
169   const std::string extension_id_;
170 
171   // Owning location manager.
172   LocationManager* location_manager_;
173 
174   // Holds Update Policies.
175   typedef std::vector<scoped_refptr<updatepolicy::UpdatePolicy> >
176       UpdatePolicyVector;
177   UpdatePolicyVector update_policies_;
178 
179   scoped_ptr<content::GeolocationProvider::Subscription>
180       geolocation_subscription_;
181 
182   DISALLOW_COPY_AND_ASSIGN(LocationRequest);
183 };
184 
LocationRequest(LocationManager * location_manager,const std::string & extension_id,const std::string & request_name,const double * distance_update_threshold_meters,const double * time_between_updates_ms)185 LocationRequest::LocationRequest(
186     LocationManager* location_manager,
187     const std::string& extension_id,
188     const std::string& request_name,
189     const double* distance_update_threshold_meters,
190     const double* time_between_updates_ms)
191     : request_name_(request_name),
192       extension_id_(extension_id),
193       location_manager_(location_manager) {
194   // TODO(vadimt): use request_info.
195   if (time_between_updates_ms) {
196     update_policies_.push_back(
197         new updatepolicy::TimeBasedUpdatePolicy(
198             *time_between_updates_ms));
199   }
200 
201   if (distance_update_threshold_meters) {
202     update_policies_.push_back(
203         new updatepolicy::DistanceBasedUpdatePolicy(
204               *distance_update_threshold_meters));
205   }
206 
207   // TODO(vadimt): This can get a location cached by GeolocationProvider,
208   // contrary to the API definition which says that creating a location watch
209   // will get new location.
210   geolocation_subscription_ = content::GeolocationProvider::GetInstance()->
211       AddLocationUpdateCallback(
212           base::Bind(&LocationRequest::OnLocationUpdate,
213                      base::Unretained(this)),
214           true);
215 }
216 
~LocationRequest()217 LocationRequest::~LocationRequest() {
218 }
219 
OnLocationUpdate(const content::Geoposition & position)220 void LocationRequest::OnLocationUpdate(const content::Geoposition& position) {
221   if (ShouldSendUpdate(position)) {
222     OnPositionReported(position);
223     location_manager_->SendLocationUpdate(
224         extension_id_, request_name_, position);
225   }
226 }
227 
ShouldSendUpdate(const content::Geoposition & position)228 bool LocationRequest::ShouldSendUpdate(const content::Geoposition& position) {
229   for (UpdatePolicyVector::iterator it = update_policies_.begin();
230        it != update_policies_.end();
231        ++it) {
232     if (!((*it)->ShouldSendUpdate(position))) {
233       return false;
234     }
235   }
236   return true;
237 }
238 
OnPositionReported(const content::Geoposition & position)239 void LocationRequest::OnPositionReported(const content::Geoposition& position) {
240   for (UpdatePolicyVector::iterator it = update_policies_.begin();
241        it != update_policies_.end();
242        ++it) {
243     (*it)->OnPositionReported(position);
244   }
245 }
246 
LocationManager(content::BrowserContext * context)247 LocationManager::LocationManager(content::BrowserContext* context)
248     : browser_context_(context), extension_registry_observer_(this) {
249   extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_));
250 }
251 
AddLocationRequest(const std::string & extension_id,const std::string & request_name,const double * distance_update_threshold_meters,const double * time_between_updates_ms)252 void LocationManager::AddLocationRequest(
253     const std::string& extension_id,
254     const std::string& request_name,
255     const double* distance_update_threshold_meters,
256     const double* time_between_updates_ms) {
257   DCHECK_CURRENTLY_ON(BrowserThread::UI);
258   // TODO(vadimt): Consider resuming requests after restarting the browser.
259 
260   // Override any old request with the same name.
261   RemoveLocationRequest(extension_id, request_name);
262 
263   LocationRequestPointer location_request =
264       new LocationRequest(this,
265                           extension_id,
266                           request_name,
267                           distance_update_threshold_meters,
268                           time_between_updates_ms);
269   location_requests_.insert(
270       LocationRequestMap::value_type(extension_id, location_request));
271 }
272 
RemoveLocationRequest(const std::string & extension_id,const std::string & name)273 void LocationManager::RemoveLocationRequest(const std::string& extension_id,
274                                             const std::string& name) {
275   DCHECK_CURRENTLY_ON(BrowserThread::UI);
276 
277   std::pair<LocationRequestMap::iterator, LocationRequestMap::iterator>
278       extension_range = location_requests_.equal_range(extension_id);
279 
280   for (LocationRequestMap::iterator it = extension_range.first;
281        it != extension_range.second;
282        ++it) {
283     if (it->second->request_name() == name) {
284       location_requests_.erase(it);
285       return;
286     }
287   }
288 }
289 
~LocationManager()290 LocationManager::~LocationManager() {
291 }
292 
GeopositionToApiCoordinates(const content::Geoposition & position,location::Coordinates * coordinates)293 void LocationManager::GeopositionToApiCoordinates(
294       const content::Geoposition& position,
295       location::Coordinates* coordinates) {
296   coordinates->latitude = position.latitude;
297   coordinates->longitude = position.longitude;
298   if (position.altitude > -10000.)
299     coordinates->altitude.reset(new double(position.altitude));
300   coordinates->accuracy = position.accuracy;
301   if (position.altitude_accuracy >= 0.) {
302     coordinates->altitude_accuracy.reset(
303         new double(position.altitude_accuracy));
304   }
305   if (position.heading >= 0. && position.heading <= 360.)
306     coordinates->heading.reset(new double(position.heading));
307   if (position.speed >= 0.)
308     coordinates->speed.reset(new double(position.speed));
309 }
310 
SendLocationUpdate(const std::string & extension_id,const std::string & request_name,const content::Geoposition & position)311 void LocationManager::SendLocationUpdate(
312     const std::string& extension_id,
313     const std::string& request_name,
314     const content::Geoposition& position) {
315   DCHECK_CURRENTLY_ON(BrowserThread::UI);
316 
317   scoped_ptr<base::ListValue> args(new base::ListValue());
318   std::string event_name;
319 
320   if (position.Validate() &&
321       position.error_code == content::Geoposition::ERROR_CODE_NONE) {
322     // Set data for onLocationUpdate event.
323     location::Location location;
324     location.name = request_name;
325     GeopositionToApiCoordinates(position, &location.coords);
326     location.timestamp = position.timestamp.ToJsTime();
327 
328     args->Append(location.ToValue().release());
329     event_name = location::OnLocationUpdate::kEventName;
330   } else {
331     // Set data for onLocationError event.
332     // TODO(vadimt): Set name.
333     args->AppendString(position.error_message);
334     event_name = location::OnLocationError::kEventName;
335   }
336 
337   scoped_ptr<Event> event(new Event(event_name, args.Pass()));
338 
339   EventRouter::Get(browser_context_)
340       ->DispatchEventToExtension(extension_id, event.Pass());
341 }
342 
OnExtensionLoaded(content::BrowserContext * browser_context,const Extension * extension)343 void LocationManager::OnExtensionLoaded(
344     content::BrowserContext* browser_context,
345     const Extension* extension) {
346   // Grants permission to use geolocation once an extension with "location"
347   // permission is loaded.
348   if (extension->permissions_data()->HasAPIPermission(
349           APIPermission::kLocation)) {
350     content::GeolocationProvider::GetInstance()
351         ->UserDidOptIntoLocationServices();
352   }
353 }
354 
OnExtensionUnloaded(content::BrowserContext * browser_context,const Extension * extension,UnloadedExtensionInfo::Reason reason)355 void LocationManager::OnExtensionUnloaded(
356     content::BrowserContext* browser_context,
357     const Extension* extension,
358     UnloadedExtensionInfo::Reason reason) {
359   // Delete all requests from the unloaded extension.
360   location_requests_.erase(extension->id());
361 }
362 
363 static base::LazyInstance<BrowserContextKeyedAPIFactory<LocationManager> >
364     g_factory = LAZY_INSTANCE_INITIALIZER;
365 
366 // static
367 BrowserContextKeyedAPIFactory<LocationManager>*
GetFactoryInstance()368 LocationManager::GetFactoryInstance() {
369   return g_factory.Pointer();
370 }
371 
372 // static
Get(content::BrowserContext * context)373 LocationManager* LocationManager::Get(content::BrowserContext* context) {
374   return BrowserContextKeyedAPIFactory<LocationManager>::Get(context);
375 }
376 
377 }  // namespace extensions
378