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 17namespace content { 18 19GeolocationProvider* GeolocationProvider::GetInstance() { 20 return GeolocationProviderImpl::GetInstance(); 21} 22 23scoped_ptr<GeolocationProvider::Subscription> 24GeolocationProviderImpl::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 43void 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 51void GeolocationProviderImpl::OverrideLocationForTesting( 52 const Geoposition& position) { 53 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 54 ignore_location_updates_ = true; 55 NotifyClients(position); 56} 57 58void 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 69GeolocationProviderImpl* GeolocationProviderImpl::GetInstance() { 70 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 71 return Singleton<GeolocationProviderImpl>::get(); 72} 73 74GeolocationProviderImpl::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 88GeolocationProviderImpl::~GeolocationProviderImpl() { 89 Stop(); 90 DCHECK(!arbitrator_); 91} 92 93bool GeolocationProviderImpl::OnGeolocationThread() const { 94 return base::MessageLoop::current() == message_loop(); 95} 96 97void 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 127void GeolocationProviderImpl::StopProviders() { 128 DCHECK(OnGeolocationThread()); 129 DCHECK(arbitrator_); 130 arbitrator_->StopProviders(); 131} 132 133void GeolocationProviderImpl::StartProviders(bool use_high_accuracy) { 134 DCHECK(OnGeolocationThread()); 135 DCHECK(arbitrator_); 136 arbitrator_->StartProviders(use_high_accuracy); 137} 138 139void 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 153void 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 162void GeolocationProviderImpl::Init() { 163 DCHECK(OnGeolocationThread()); 164 DCHECK(!arbitrator_); 165 arbitrator_ = CreateArbitrator(); 166} 167 168void GeolocationProviderImpl::CleanUp() { 169 DCHECK(OnGeolocationThread()); 170 delete arbitrator_; 171 arbitrator_ = NULL; 172} 173 174LocationArbitrator* 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