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