1 // Copyright (c) 2013 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 "chrome/browser/extensions/api/location/location_manager.h"
10 #include "base/bind.h"
11 #include "base/lazy_instance.h"
12 #include "base/time/time.h"
13 #include "chrome/common/extensions/api/location.h"
14 #include "content/public/browser/browser_thread.h"
15 #include "content/public/browser/geolocation_provider.h"
16 #include "content/public/common/geoposition.h"
17 #include "extensions/browser/event_router.h"
18 #include "extensions/browser/extension_registry.h"
19 #include "extensions/browser/extension_system.h"
20 #include "extensions/common/extension.h"
21 #include "extensions/common/permissions/permission_set.h"
22 #include "extensions/common/permissions/permissions_data.h"
24 using content::BrowserThread
;
26 // TODO(vadimt): Add tests.
27 namespace extensions
{
29 namespace location
= api::location
;
31 namespace updatepolicy
{
33 // Base class for all update policies for sending a location.
34 class UpdatePolicy
: public base::RefCounted
<UpdatePolicy
> {
38 // True if the caller should send an update based off of this policy.
39 virtual bool ShouldSendUpdate(const content::Geoposition
&) const = 0;
41 // Updates any policy state on reporting a position.
42 virtual void OnPositionReported(const content::Geoposition
&) = 0;
45 virtual ~UpdatePolicy() {}
48 friend class base::RefCounted
<UpdatePolicy
>;
49 DISALLOW_COPY_AND_ASSIGN(UpdatePolicy
);
52 // A policy that controls sending an update below a distance threshold.
53 class DistanceBasedUpdatePolicy
: public UpdatePolicy
{
55 explicit DistanceBasedUpdatePolicy(double distance_update_threshold_meters
) :
56 distance_update_threshold_meters_(distance_update_threshold_meters
)
59 // UpdatePolicy Implementation
60 bool ShouldSendUpdate(const content::Geoposition
& position
) const override
{
61 return !last_updated_position_
.Validate() ||
62 Distance(position
.latitude
,
64 last_updated_position_
.latitude
,
65 last_updated_position_
.longitude
) >
66 distance_update_threshold_meters_
;
69 void OnPositionReported(const content::Geoposition
& position
) override
{
70 last_updated_position_
= position
;
74 ~DistanceBasedUpdatePolicy() override
{}
76 // Calculates the distance between two latitude and longitude points.
77 static double Distance(const double latitude1
,
78 const double longitude1
,
79 const double latitude2
,
80 const double longitude2
) {
81 // The earth has a radius of about 6371 km.
82 const double kRadius
= 6371000;
83 const double kPi
= 3.14159265358979323846;
84 const double kDegreesToRadians
= kPi
/ 180.0;
87 const double latitude1Rad
= latitude1
* kDegreesToRadians
;
88 const double latitude2Rad
= latitude2
* kDegreesToRadians
;
89 const double latitudeDistRad
= latitude2Rad
- latitude1Rad
;
90 const double longitudeDistRad
= (longitude2
- longitude1
) *
93 // The Haversine Formula determines the great circle distance
94 // between two points on a sphere.
95 const double chordLengthSquared
= pow(sin(latitudeDistRad
/ 2.0), 2) +
96 (pow(sin(longitudeDistRad
/ 2.0), 2) *
99 const double angularDistance
= 2.0 * atan2(sqrt(chordLengthSquared
),
100 sqrt(1.0 - chordLengthSquared
));
101 return kRadius
* angularDistance
;
104 const double distance_update_threshold_meters_
;
105 content::Geoposition last_updated_position_
;
107 DISALLOW_COPY_AND_ASSIGN(DistanceBasedUpdatePolicy
);
110 // A policy that controls sending an update above a time threshold.
111 class TimeBasedUpdatePolicy
: public UpdatePolicy
{
113 explicit TimeBasedUpdatePolicy(double time_between_updates_ms
) :
114 time_between_updates_ms_(time_between_updates_ms
)
117 // UpdatePolicy Implementation
118 bool ShouldSendUpdate(const content::Geoposition
&) const override
{
119 return (base::Time::Now() - last_update_time_
).InMilliseconds() >
120 time_between_updates_ms_
;
123 void OnPositionReported(const content::Geoposition
&) override
{
124 last_update_time_
= base::Time::Now();
128 ~TimeBasedUpdatePolicy() override
{}
130 base::Time last_update_time_
;
131 const double time_between_updates_ms_
;
133 DISALLOW_COPY_AND_ASSIGN(TimeBasedUpdatePolicy
);
136 } // namespace updatepolicy
138 // Request created by chrome.location.watchLocation() call.
139 class LocationRequest
: public base::RefCounted
<LocationRequest
> {
142 LocationManager
* location_manager
,
143 const std::string
& extension_id
,
144 const std::string
& request_name
,
145 const double* distance_update_threshold_meters
,
146 const double* time_between_updates_ms
);
148 const std::string
& request_name() const { return request_name_
; }
151 friend class base::RefCounted
<LocationRequest
>;
154 void OnLocationUpdate(const content::Geoposition
& position
);
156 // Determines if all policies say to send a position update.
157 // If there are no policies, this always says yes.
158 bool ShouldSendUpdate(const content::Geoposition
& position
);
160 // Updates the policies on sending a position update.
161 void OnPositionReported(const content::Geoposition
& position
);
164 const std::string request_name_
;
166 // Id of the owner extension.
167 const std::string extension_id_
;
169 // Owning location manager.
170 LocationManager
* location_manager_
;
172 // Holds Update Policies.
173 typedef std::vector
<scoped_refptr
<updatepolicy::UpdatePolicy
> >
175 UpdatePolicyVector update_policies_
;
177 scoped_ptr
<content::GeolocationProvider::Subscription
>
178 geolocation_subscription_
;
180 DISALLOW_COPY_AND_ASSIGN(LocationRequest
);
183 LocationRequest::LocationRequest(
184 LocationManager
* location_manager
,
185 const std::string
& extension_id
,
186 const std::string
& request_name
,
187 const double* distance_update_threshold_meters
,
188 const double* time_between_updates_ms
)
189 : request_name_(request_name
),
190 extension_id_(extension_id
),
191 location_manager_(location_manager
) {
192 // TODO(vadimt): use request_info.
193 if (time_between_updates_ms
) {
194 update_policies_
.push_back(
195 new updatepolicy::TimeBasedUpdatePolicy(
196 *time_between_updates_ms
));
199 if (distance_update_threshold_meters
) {
200 update_policies_
.push_back(
201 new updatepolicy::DistanceBasedUpdatePolicy(
202 *distance_update_threshold_meters
));
205 // TODO(vadimt): This can get a location cached by GeolocationProvider,
206 // contrary to the API definition which says that creating a location watch
207 // will get new location.
208 geolocation_subscription_
= content::GeolocationProvider::GetInstance()->
209 AddLocationUpdateCallback(
210 base::Bind(&LocationRequest::OnLocationUpdate
,
211 base::Unretained(this)),
215 LocationRequest::~LocationRequest() {
218 void LocationRequest::OnLocationUpdate(const content::Geoposition
& position
) {
219 if (ShouldSendUpdate(position
)) {
220 OnPositionReported(position
);
221 location_manager_
->SendLocationUpdate(
222 extension_id_
, request_name_
, position
);
226 bool LocationRequest::ShouldSendUpdate(const content::Geoposition
& position
) {
227 for (UpdatePolicyVector::iterator it
= update_policies_
.begin();
228 it
!= update_policies_
.end();
230 if (!((*it
)->ShouldSendUpdate(position
))) {
237 void LocationRequest::OnPositionReported(const content::Geoposition
& position
) {
238 for (UpdatePolicyVector::iterator it
= update_policies_
.begin();
239 it
!= update_policies_
.end();
241 (*it
)->OnPositionReported(position
);
245 LocationManager::LocationManager(content::BrowserContext
* context
)
246 : browser_context_(context
), extension_registry_observer_(this) {
247 extension_registry_observer_
.Add(ExtensionRegistry::Get(browser_context_
));
250 void LocationManager::AddLocationRequest(
251 const std::string
& extension_id
,
252 const std::string
& request_name
,
253 const double* distance_update_threshold_meters
,
254 const double* time_between_updates_ms
) {
255 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
256 // TODO(vadimt): Consider resuming requests after restarting the browser.
258 // Override any old request with the same name.
259 RemoveLocationRequest(extension_id
, request_name
);
261 LocationRequestPointer location_request
=
262 new LocationRequest(this,
265 distance_update_threshold_meters
,
266 time_between_updates_ms
);
267 location_requests_
.insert(
268 LocationRequestMap::value_type(extension_id
, location_request
));
271 void LocationManager::RemoveLocationRequest(const std::string
& extension_id
,
272 const std::string
& name
) {
273 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
275 std::pair
<LocationRequestMap::iterator
, LocationRequestMap::iterator
>
276 extension_range
= location_requests_
.equal_range(extension_id
);
278 for (LocationRequestMap::iterator it
= extension_range
.first
;
279 it
!= extension_range
.second
;
281 if (it
->second
->request_name() == name
) {
282 location_requests_
.erase(it
);
288 LocationManager::~LocationManager() {
291 void LocationManager::GeopositionToApiCoordinates(
292 const content::Geoposition
& position
,
293 location::Coordinates
* coordinates
) {
294 coordinates
->latitude
= position
.latitude
;
295 coordinates
->longitude
= position
.longitude
;
296 if (position
.altitude
> -10000.)
297 coordinates
->altitude
.reset(new double(position
.altitude
));
298 coordinates
->accuracy
= position
.accuracy
;
299 if (position
.altitude_accuracy
>= 0.) {
300 coordinates
->altitude_accuracy
.reset(
301 new double(position
.altitude_accuracy
));
303 if (position
.heading
>= 0. && position
.heading
<= 360.)
304 coordinates
->heading
.reset(new double(position
.heading
));
305 if (position
.speed
>= 0.)
306 coordinates
->speed
.reset(new double(position
.speed
));
309 void LocationManager::SendLocationUpdate(
310 const std::string
& extension_id
,
311 const std::string
& request_name
,
312 const content::Geoposition
& position
) {
313 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
315 scoped_ptr
<base::ListValue
> args(new base::ListValue());
316 events::HistogramValue histogram_value
= events::UNKNOWN
;
317 std::string event_name
;
319 if (position
.Validate() &&
320 position
.error_code
== content::Geoposition::ERROR_CODE_NONE
) {
321 // Set data for onLocationUpdate event.
322 location::Location location
;
323 location
.name
= request_name
;
324 GeopositionToApiCoordinates(position
, &location
.coords
);
325 location
.timestamp
= position
.timestamp
.ToJsTime();
327 args
->Append(location
.ToValue().release());
328 histogram_value
= events::LOCATION_ON_LOCATION_UPDATE
;
329 event_name
= location::OnLocationUpdate::kEventName
;
331 // Set data for onLocationError event.
332 // TODO(vadimt): Set name.
333 args
->AppendString(position
.error_message
);
334 histogram_value
= events::LOCATION_ON_LOCATION_ERROR
;
335 event_name
= location::OnLocationError::kEventName
;
338 scoped_ptr
<Event
> event(new Event(histogram_value
, event_name
, args
.Pass()));
340 EventRouter::Get(browser_context_
)
341 ->DispatchEventToExtension(extension_id
, event
.Pass());
344 void LocationManager::OnExtensionLoaded(
345 content::BrowserContext
* browser_context
,
346 const Extension
* extension
) {
347 // Grants permission to use geolocation once an extension with "location"
348 // permission is loaded.
349 if (extension
->permissions_data()->HasAPIPermission(
350 APIPermission::kLocation
)) {
351 content::GeolocationProvider::GetInstance()
352 ->UserDidOptIntoLocationServices();
356 void LocationManager::OnExtensionUnloaded(
357 content::BrowserContext
* browser_context
,
358 const Extension
* extension
,
359 UnloadedExtensionInfo::Reason reason
) {
360 // Delete all requests from the unloaded extension.
361 location_requests_
.erase(extension
->id());
364 static base::LazyInstance
<BrowserContextKeyedAPIFactory
<LocationManager
> >
365 g_factory
= LAZY_INSTANCE_INITIALIZER
;
368 BrowserContextKeyedAPIFactory
<LocationManager
>*
369 LocationManager::GetFactoryInstance() {
370 return g_factory
.Pointer();
374 LocationManager
* LocationManager::Get(content::BrowserContext
* context
) {
375 return BrowserContextKeyedAPIFactory
<LocationManager
>::Get(context
);
378 } // namespace extensions