Home | History | Annotate | Download | only in location
      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:
     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:
     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:
     55   explicit DistanceBasedUpdatePolicy(double distance_update_threshold_meters) :
     56       distance_update_threshold_meters_(distance_update_threshold_meters)
     57   {}
     58 
     59   // UpdatePolicy Implementation
     60   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 
     70   virtual void OnPositionReported(const content::Geoposition& position)
     71       OVERRIDE {
     72     last_updated_position_ = position;
     73   }
     74 
     75  private:
     76   virtual ~DistanceBasedUpdatePolicy() {}
     77 
     78   // Calculates the distance between two latitude and longitude points.
     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:
    115   explicit TimeBasedUpdatePolicy(double time_between_updates_ms) :
    116       time_between_updates_ms_(time_between_updates_ms)
    117   {}
    118 
    119   // UpdatePolicy Implementation
    120   virtual bool ShouldSendUpdate(const content::Geoposition&) const OVERRIDE {
    121     return (base::Time::Now() - last_update_time_).InMilliseconds() >
    122         time_between_updates_ms_;
    123   }
    124 
    125   virtual void OnPositionReported(const content::Geoposition&) OVERRIDE {
    126     last_update_time_ = base::Time::Now();
    127   }
    128 
    129  private:
    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 
    150   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 
    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 
    217 LocationRequest::~LocationRequest() {
    218 }
    219 
    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 
    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 
    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 
    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 
    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 
    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 
    290 LocationManager::~LocationManager() {
    291 }
    292 
    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 
    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 
    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 
    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>*
    368 LocationManager::GetFactoryInstance() {
    369   return g_factory.Pointer();
    370 }
    371 
    372 // static
    373 LocationManager* LocationManager::Get(content::BrowserContext* context) {
    374   return BrowserContextKeyedAPIFactory<LocationManager>::Get(context);
    375 }
    376 
    377 }  // namespace extensions
    378