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