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> {
36 explicit 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 std::string event_name;
318 if (position.Validate() &&
319 position.error_code == content::Geoposition::ERROR_CODE_NONE) {
320 // Set data for onLocationUpdate event.
321 location::Location location;
322 location.name = request_name;
323 GeopositionToApiCoordinates(position, &location.coords);
324 location.timestamp = position.timestamp.ToJsTime();
326 args->Append(location.ToValue().release());
327 event_name = location::OnLocationUpdate::kEventName;
329 // Set data for onLocationError event.
330 // TODO(vadimt): Set name.
331 args->AppendString(position.error_message);
332 event_name = location::OnLocationError::kEventName;
335 scoped_ptr<Event> event(new Event(event_name, args.Pass()));
337 EventRouter::Get(browser_context_)
338 ->DispatchEventToExtension(extension_id, event.Pass());
341 void LocationManager::OnExtensionLoaded(
342 content::BrowserContext* browser_context,
343 const Extension* extension) {
344 // Grants permission to use geolocation once an extension with "location"
345 // permission is loaded.
346 if (extension->permissions_data()->HasAPIPermission(
347 APIPermission::kLocation)) {
348 content::GeolocationProvider::GetInstance()
349 ->UserDidOptIntoLocationServices();
353 void LocationManager::OnExtensionUnloaded(
354 content::BrowserContext* browser_context,
355 const Extension* extension,
356 UnloadedExtensionInfo::Reason reason) {
357 // Delete all requests from the unloaded extension.
358 location_requests_.erase(extension->id());
361 static base::LazyInstance<BrowserContextKeyedAPIFactory<LocationManager> >
362 g_factory = LAZY_INSTANCE_INITIALIZER;
365 BrowserContextKeyedAPIFactory<LocationManager>*
366 LocationManager::GetFactoryInstance() {
367 return g_factory.Pointer();
371 LocationManager* LocationManager::Get(content::BrowserContext* context) {
372 return BrowserContextKeyedAPIFactory<LocationManager>::Get(context);
375 } // namespace extensions