• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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