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/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 "chrome/common/extensions/permissions/permission_set.h"
     19 #include "content/public/browser/browser_thread.h"
     20 #include "content/public/browser/geolocation_provider.h"
     21 #include "content/public/browser/notification_details.h"
     22 #include "content/public/browser/notification_source.h"
     23 #include "content/public/common/geoposition.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";
    386   } else {
    387     // Set data for onLocationError event.
    388     // TODO(vadimt): Set name.
    389     args->AppendString(position.error_message);
    390     event_name = "location.onLocationError";
    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
    446