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