Home | History | Annotate | Download | only in geolocation
      1 // Copyright (c) 2012 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 "content/browser/geolocation/geolocation_provider_impl.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/bind_helpers.h"
      9 #include "base/callback.h"
     10 #include "base/location.h"
     11 #include "base/logging.h"
     12 #include "base/memory/singleton.h"
     13 #include "base/message_loop/message_loop.h"
     14 #include "content/browser/geolocation/location_arbitrator_impl.h"
     15 #include "content/public/browser/browser_thread.h"
     16 
     17 namespace content {
     18 
     19 namespace {
     20 void OverrideLocationForTestingOnIOThread(
     21     const Geoposition& position,
     22     const base::Closure& completion_callback,
     23     scoped_refptr<base::MessageLoopProxy> callback_loop) {
     24   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     25   GeolocationProviderImpl::GetInstance()->OverrideLocationForTesting(position);
     26   callback_loop->PostTask(FROM_HERE, completion_callback);
     27 }
     28 }  // namespace
     29 
     30 GeolocationProvider* GeolocationProvider::GetInstance() {
     31   return GeolocationProviderImpl::GetInstance();
     32 }
     33 
     34 void GeolocationProvider::OverrideLocationForTesting(
     35     const Geoposition& position,
     36     const base::Closure& completion_callback) {
     37   base::Closure closure = base::Bind(&OverrideLocationForTestingOnIOThread,
     38                                      position,
     39                                      completion_callback,
     40                                      base::MessageLoopProxy::current());
     41   if (!BrowserThread::CurrentlyOn(BrowserThread::IO))
     42     BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, closure);
     43   else
     44     closure.Run();
     45 }
     46 
     47 void GeolocationProviderImpl::AddLocationUpdateCallback(
     48     const LocationUpdateCallback& callback, bool use_high_accuracy) {
     49   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     50   bool found = false;
     51   CallbackList::iterator i = callbacks_.begin();
     52   for (; i != callbacks_.end(); ++i) {
     53     if (i->first.Equals(callback)) {
     54       i->second = use_high_accuracy;
     55       found = true;
     56       break;
     57     }
     58   }
     59   if (!found)
     60     callbacks_.push_back(std::make_pair(callback, use_high_accuracy));
     61 
     62   OnClientsChanged();
     63   if (position_.Validate() ||
     64       position_.error_code != Geoposition::ERROR_CODE_NONE) {
     65     callback.Run(position_);
     66   }
     67 }
     68 
     69 bool GeolocationProviderImpl::RemoveLocationUpdateCallback(
     70     const LocationUpdateCallback& callback) {
     71   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     72   bool removed = false;
     73   CallbackList::iterator i = callbacks_.begin();
     74   for (; i != callbacks_.end(); ++i) {
     75     if (i->first.Equals(callback)) {
     76       callbacks_.erase(i);
     77       removed = true;
     78       break;
     79     }
     80   }
     81   if (removed)
     82     OnClientsChanged();
     83   return removed;
     84 }
     85 
     86 void GeolocationProviderImpl::UserDidOptIntoLocationServices() {
     87   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     88   bool was_permission_granted = user_did_opt_into_location_services_;
     89   user_did_opt_into_location_services_ = true;
     90   if (IsRunning() && !was_permission_granted)
     91     InformProvidersPermissionGranted();
     92 }
     93 
     94 bool GeolocationProviderImpl::LocationServicesOptedIn() const {
     95   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     96   return user_did_opt_into_location_services_;
     97 }
     98 
     99 void GeolocationProviderImpl::OnLocationUpdate(const Geoposition& position) {
    100   DCHECK(OnGeolocationThread());
    101   // Will be true only in testing.
    102   if (ignore_location_updates_)
    103     return;
    104   BrowserThread::PostTask(BrowserThread::IO,
    105                           FROM_HERE,
    106                           base::Bind(&GeolocationProviderImpl::NotifyClients,
    107                                      base::Unretained(this), position));
    108 }
    109 
    110 void GeolocationProviderImpl::OverrideLocationForTesting(
    111     const Geoposition& position) {
    112   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    113   position_ = position;
    114   ignore_location_updates_ = true;
    115   NotifyClients(position);
    116 }
    117 
    118 GeolocationProviderImpl* GeolocationProviderImpl::GetInstance() {
    119   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    120   return Singleton<GeolocationProviderImpl>::get();
    121 }
    122 
    123 GeolocationProviderImpl::GeolocationProviderImpl()
    124     : base::Thread("Geolocation"),
    125       user_did_opt_into_location_services_(false),
    126       ignore_location_updates_(false),
    127       arbitrator_(NULL) {
    128   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    129 }
    130 
    131 GeolocationProviderImpl::~GeolocationProviderImpl() {
    132   // All callbacks should have unregistered before this singleton is destructed.
    133   DCHECK(callbacks_.empty());
    134   Stop();
    135   DCHECK(!arbitrator_);
    136 }
    137 
    138 bool GeolocationProviderImpl::OnGeolocationThread() const {
    139   return base::MessageLoop::current() == message_loop();
    140 }
    141 
    142 void GeolocationProviderImpl::OnClientsChanged() {
    143   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    144   base::Closure task;
    145   if (callbacks_.empty()) {
    146     DCHECK(IsRunning());
    147     // We have no more observers, so we clear the cached geoposition so that
    148     // when the next observer is added we will not provide a stale position.
    149     position_ = Geoposition();
    150     task = base::Bind(&GeolocationProviderImpl::StopProviders,
    151                       base::Unretained(this));
    152   } else {
    153     if (!IsRunning()) {
    154       Start();
    155       if (LocationServicesOptedIn())
    156         InformProvidersPermissionGranted();
    157     }
    158     // Determine a set of options that satisfies all clients.
    159     bool use_high_accuracy = false;
    160     CallbackList::iterator i = callbacks_.begin();
    161     for (; i != callbacks_.end(); ++i) {
    162       if (i->second) {
    163         use_high_accuracy = true;
    164         break;
    165       }
    166     }
    167 
    168     // Send the current options to the providers as they may have changed.
    169     task = base::Bind(&GeolocationProviderImpl::StartProviders,
    170                       base::Unretained(this),
    171                       use_high_accuracy);
    172   }
    173 
    174   message_loop()->PostTask(FROM_HERE, task);
    175 }
    176 
    177 void GeolocationProviderImpl::StopProviders() {
    178   DCHECK(OnGeolocationThread());
    179   DCHECK(arbitrator_);
    180   arbitrator_->StopProviders();
    181 }
    182 
    183 void GeolocationProviderImpl::StartProviders(bool use_high_accuracy) {
    184   DCHECK(OnGeolocationThread());
    185   DCHECK(arbitrator_);
    186   arbitrator_->StartProviders(use_high_accuracy);
    187 }
    188 
    189 void GeolocationProviderImpl::InformProvidersPermissionGranted() {
    190   DCHECK(IsRunning());
    191   if (!OnGeolocationThread()) {
    192     message_loop()->PostTask(
    193         FROM_HERE,
    194         base::Bind(&GeolocationProviderImpl::InformProvidersPermissionGranted,
    195                    base::Unretained(this)));
    196     return;
    197   }
    198   DCHECK(OnGeolocationThread());
    199   DCHECK(arbitrator_);
    200   arbitrator_->OnPermissionGranted();
    201 }
    202 
    203 void GeolocationProviderImpl::NotifyClients(const Geoposition& position) {
    204   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    205   DCHECK(position.Validate() ||
    206          position.error_code != Geoposition::ERROR_CODE_NONE);
    207   position_ = position;
    208   CallbackList::const_iterator it = callbacks_.begin();
    209   while (it != callbacks_.end()) {
    210     // Advance iterator before calling the observer to guard against synchronous
    211     // unregister.
    212     LocationUpdateCallback callback = it->first;
    213     ++it;
    214     callback.Run(position_);
    215   }
    216 }
    217 
    218 void GeolocationProviderImpl::Init() {
    219   DCHECK(OnGeolocationThread());
    220   DCHECK(!arbitrator_);
    221   arbitrator_ = CreateArbitrator();
    222 }
    223 
    224 void GeolocationProviderImpl::CleanUp() {
    225   DCHECK(OnGeolocationThread());
    226   delete arbitrator_;
    227   arbitrator_ = NULL;
    228 }
    229 
    230 LocationArbitrator* GeolocationProviderImpl::CreateArbitrator() {
    231   LocationArbitratorImpl::LocationUpdateCallback callback = base::Bind(
    232       &GeolocationProviderImpl::OnLocationUpdate, base::Unretained(this));
    233   return new LocationArbitratorImpl(callback);
    234 }
    235 
    236 }  // namespace content
    237