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.
5 #include "content/browser/geolocation/geolocation_provider_impl.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"
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);
30 GeolocationProvider* GeolocationProvider::GetInstance() {
31 return GeolocationProviderImpl::GetInstance();
34 void GeolocationProvider::OverrideLocationForTesting(
35 const Geoposition& position,
36 const base::Closure& completion_callback) {
37 base::Closure closure = base::Bind(&OverrideLocationForTestingOnIOThread,
40 base::MessageLoopProxy::current());
41 if (!BrowserThread::CurrentlyOn(BrowserThread::IO))
42 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, closure);
47 void GeolocationProviderImpl::AddLocationUpdateCallback(
48 const LocationUpdateCallback& callback, bool use_high_accuracy) {
49 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
51 CallbackList::iterator i = callbacks_.begin();
52 for (; i != callbacks_.end(); ++i) {
53 if (i->first.Equals(callback)) {
54 i->second = use_high_accuracy;
60 callbacks_.push_back(std::make_pair(callback, use_high_accuracy));
63 if (position_.Validate() ||
64 position_.error_code != Geoposition::ERROR_CODE_NONE) {
65 callback.Run(position_);
69 bool GeolocationProviderImpl::RemoveLocationUpdateCallback(
70 const LocationUpdateCallback& callback) {
71 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
73 CallbackList::iterator i = callbacks_.begin();
74 for (; i != callbacks_.end(); ++i) {
75 if (i->first.Equals(callback)) {
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();
94 bool GeolocationProviderImpl::LocationServicesOptedIn() const {
95 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
96 return user_did_opt_into_location_services_;
99 void GeolocationProviderImpl::OnLocationUpdate(const Geoposition& position) {
100 DCHECK(OnGeolocationThread());
101 // Will be true only in testing.
102 if (ignore_location_updates_)
104 BrowserThread::PostTask(BrowserThread::IO,
106 base::Bind(&GeolocationProviderImpl::NotifyClients,
107 base::Unretained(this), position));
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);
118 GeolocationProviderImpl* GeolocationProviderImpl::GetInstance() {
119 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
120 return Singleton<GeolocationProviderImpl>::get();
123 GeolocationProviderImpl::GeolocationProviderImpl()
124 : base::Thread("Geolocation"),
125 user_did_opt_into_location_services_(false),
126 ignore_location_updates_(false),
128 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
131 GeolocationProviderImpl::~GeolocationProviderImpl() {
132 // All callbacks should have unregistered before this singleton is destructed.
133 DCHECK(callbacks_.empty());
135 DCHECK(!arbitrator_);
138 bool GeolocationProviderImpl::OnGeolocationThread() const {
139 return base::MessageLoop::current() == message_loop();
142 void GeolocationProviderImpl::OnClientsChanged() {
143 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
145 if (callbacks_.empty()) {
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));
155 if (LocationServicesOptedIn())
156 InformProvidersPermissionGranted();
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) {
163 use_high_accuracy = true;
168 // Send the current options to the providers as they may have changed.
169 task = base::Bind(&GeolocationProviderImpl::StartProviders,
170 base::Unretained(this),
174 message_loop()->PostTask(FROM_HERE, task);
177 void GeolocationProviderImpl::StopProviders() {
178 DCHECK(OnGeolocationThread());
180 arbitrator_->StopProviders();
183 void GeolocationProviderImpl::StartProviders(bool use_high_accuracy) {
184 DCHECK(OnGeolocationThread());
186 arbitrator_->StartProviders(use_high_accuracy);
189 void GeolocationProviderImpl::InformProvidersPermissionGranted() {
191 if (!OnGeolocationThread()) {
192 message_loop()->PostTask(
194 base::Bind(&GeolocationProviderImpl::InformProvidersPermissionGranted,
195 base::Unretained(this)));
198 DCHECK(OnGeolocationThread());
200 arbitrator_->OnPermissionGranted();
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
212 LocationUpdateCallback callback = it->first;
214 callback.Run(position_);
218 void GeolocationProviderImpl::Init() {
219 DCHECK(OnGeolocationThread());
220 DCHECK(!arbitrator_);
221 arbitrator_ = CreateArbitrator();
224 void GeolocationProviderImpl::CleanUp() {
225 DCHECK(OnGeolocationThread());
230 LocationArbitrator* GeolocationProviderImpl::CreateArbitrator() {
231 LocationArbitratorImpl::LocationUpdateCallback callback = base::Bind(
232 &GeolocationProviderImpl::OnLocationUpdate, base::Unretained(this));
233 return new LocationArbitratorImpl(callback);
236 } // namespace content