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 GeolocationProvider* GeolocationProvider::GetInstance() {
     20   return GeolocationProviderImpl::GetInstance();
     21 }
     22 
     23 scoped_ptr<GeolocationProvider::Subscription>
     24 GeolocationProviderImpl::AddLocationUpdateCallback(
     25     const LocationUpdateCallback& callback, bool use_high_accuracy) {
     26   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     27   scoped_ptr<GeolocationProvider::Subscription> subscription;
     28   if (use_high_accuracy) {
     29     subscription = high_accuracy_callbacks_.Add(callback);
     30   } else {
     31     subscription = low_accuracy_callbacks_.Add(callback);
     32   }
     33 
     34   OnClientsChanged();
     35   if (position_.Validate() ||
     36       position_.error_code != Geoposition::ERROR_CODE_NONE) {
     37     callback.Run(position_);
     38   }
     39 
     40   return subscription.Pass();
     41 }
     42 
     43 void GeolocationProviderImpl::UserDidOptIntoLocationServices() {
     44   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     45   bool was_permission_granted = user_did_opt_into_location_services_;
     46   user_did_opt_into_location_services_ = true;
     47   if (IsRunning() && !was_permission_granted)
     48     InformProvidersPermissionGranted();
     49 }
     50 
     51 void GeolocationProviderImpl::OverrideLocationForTesting(
     52     const Geoposition& position) {
     53   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     54   ignore_location_updates_ = true;
     55   NotifyClients(position);
     56 }
     57 
     58 void GeolocationProviderImpl::OnLocationUpdate(const Geoposition& position) {
     59   DCHECK(OnGeolocationThread());
     60   // Will be true only in testing.
     61   if (ignore_location_updates_)
     62     return;
     63   BrowserThread::PostTask(BrowserThread::UI,
     64                           FROM_HERE,
     65                           base::Bind(&GeolocationProviderImpl::NotifyClients,
     66                                      base::Unretained(this), position));
     67 }
     68 
     69 GeolocationProviderImpl* GeolocationProviderImpl::GetInstance() {
     70   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     71   return Singleton<GeolocationProviderImpl>::get();
     72 }
     73 
     74 GeolocationProviderImpl::GeolocationProviderImpl()
     75     : base::Thread("Geolocation"),
     76       user_did_opt_into_location_services_(false),
     77       ignore_location_updates_(false),
     78       arbitrator_(NULL) {
     79   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     80   high_accuracy_callbacks_.set_removal_callback(
     81       base::Bind(&GeolocationProviderImpl::OnClientsChanged,
     82                  base::Unretained(this)));
     83   low_accuracy_callbacks_.set_removal_callback(
     84       base::Bind(&GeolocationProviderImpl::OnClientsChanged,
     85                  base::Unretained(this)));
     86 }
     87 
     88 GeolocationProviderImpl::~GeolocationProviderImpl() {
     89   Stop();
     90   DCHECK(!arbitrator_);
     91 }
     92 
     93 bool GeolocationProviderImpl::OnGeolocationThread() const {
     94   return base::MessageLoop::current() == message_loop();
     95 }
     96 
     97 void GeolocationProviderImpl::OnClientsChanged() {
     98   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     99   base::Closure task;
    100   if (high_accuracy_callbacks_.empty() && low_accuracy_callbacks_.empty()) {
    101     DCHECK(IsRunning());
    102     if (!ignore_location_updates_) {
    103       // We have no more observers, so we clear the cached geoposition so that
    104       // when the next observer is added we will not provide a stale position.
    105       position_ = Geoposition();
    106     }
    107     task = base::Bind(&GeolocationProviderImpl::StopProviders,
    108                       base::Unretained(this));
    109   } else {
    110     if (!IsRunning()) {
    111       Start();
    112       if (user_did_opt_into_location_services_)
    113         InformProvidersPermissionGranted();
    114     }
    115     // Determine a set of options that satisfies all clients.
    116     bool use_high_accuracy = !high_accuracy_callbacks_.empty();
    117 
    118     // Send the current options to the providers as they may have changed.
    119     task = base::Bind(&GeolocationProviderImpl::StartProviders,
    120                       base::Unretained(this),
    121                       use_high_accuracy);
    122   }
    123 
    124   message_loop()->PostTask(FROM_HERE, task);
    125 }
    126 
    127 void GeolocationProviderImpl::StopProviders() {
    128   DCHECK(OnGeolocationThread());
    129   DCHECK(arbitrator_);
    130   arbitrator_->StopProviders();
    131 }
    132 
    133 void GeolocationProviderImpl::StartProviders(bool use_high_accuracy) {
    134   DCHECK(OnGeolocationThread());
    135   DCHECK(arbitrator_);
    136   arbitrator_->StartProviders(use_high_accuracy);
    137 }
    138 
    139 void GeolocationProviderImpl::InformProvidersPermissionGranted() {
    140   DCHECK(IsRunning());
    141   if (!OnGeolocationThread()) {
    142     message_loop()->PostTask(
    143         FROM_HERE,
    144         base::Bind(&GeolocationProviderImpl::InformProvidersPermissionGranted,
    145                    base::Unretained(this)));
    146     return;
    147   }
    148   DCHECK(OnGeolocationThread());
    149   DCHECK(arbitrator_);
    150   arbitrator_->OnPermissionGranted();
    151 }
    152 
    153 void GeolocationProviderImpl::NotifyClients(const Geoposition& position) {
    154   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    155   DCHECK(position.Validate() ||
    156          position.error_code != Geoposition::ERROR_CODE_NONE);
    157   position_ = position;
    158   high_accuracy_callbacks_.Notify(position_);
    159   low_accuracy_callbacks_.Notify(position_);
    160 }
    161 
    162 void GeolocationProviderImpl::Init() {
    163   DCHECK(OnGeolocationThread());
    164   DCHECK(!arbitrator_);
    165   arbitrator_ = CreateArbitrator();
    166 }
    167 
    168 void GeolocationProviderImpl::CleanUp() {
    169   DCHECK(OnGeolocationThread());
    170   delete arbitrator_;
    171   arbitrator_ = NULL;
    172 }
    173 
    174 LocationArbitrator* GeolocationProviderImpl::CreateArbitrator() {
    175   LocationArbitratorImpl::LocationUpdateCallback callback = base::Bind(
    176       &GeolocationProviderImpl::OnLocationUpdate, base::Unretained(this));
    177   return new LocationArbitratorImpl(callback);
    178 }
    179 
    180 }  // namespace content
    181