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