1c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Copyright (c) 2013 The Chromium Authors. All rights reserved.
2c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// found in the LICENSE file.
4c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
5c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/browser/extensions/api/location/location_manager.h"
6c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
7eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include <math.h>
8eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include <vector>
9eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
10b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "base/bind.h"
11a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)#include "base/lazy_instance.h"
12eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/time/time.h"
13c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/common/extensions/api/location.h"
147d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "content/public/browser/browser_thread.h"
15b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "content/public/browser/geolocation_provider.h"
16b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "content/public/common/geoposition.h"
17f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "extensions/browser/event_router.h"
186d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)#include "extensions/browser/extension_registry.h"
195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/browser/extension_system.h"
20f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "extensions/common/extension.h"
211e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)#include "extensions/common/permissions/permission_set.h"
2246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#include "extensions/common/permissions/permissions_data.h"
23c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
247d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)using content::BrowserThread;
257d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
26c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// TODO(vadimt): Add tests.
27c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)namespace extensions {
28c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
29c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)namespace location = api::location;
30c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
31eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochnamespace updatepolicy {
32eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
33eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// Base class for all update policies for sending a location.
34eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochclass UpdatePolicy : public base::RefCounted<UpdatePolicy> {
35eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch public:
36eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  explicit UpdatePolicy() {}
37eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
38eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // True if the caller should send an update based off of this policy.
39eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  virtual bool ShouldSendUpdate(const content::Geoposition&) const = 0;
40eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
41eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // Updates any policy state on reporting a position.
42eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  virtual void OnPositionReported(const content::Geoposition&) = 0;
43eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
44eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch protected:
45eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  virtual ~UpdatePolicy() {}
46eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
47eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch private:
48eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  friend class base::RefCounted<UpdatePolicy>;
49eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  DISALLOW_COPY_AND_ASSIGN(UpdatePolicy);
50eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch};
51eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
52eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// A policy that controls sending an update below a distance threshold.
53eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochclass DistanceBasedUpdatePolicy : public UpdatePolicy {
54eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch public:
55eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  explicit DistanceBasedUpdatePolicy(double distance_update_threshold_meters) :
56eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      distance_update_threshold_meters_(distance_update_threshold_meters)
57eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  {}
58eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
59eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // UpdatePolicy Implementation
60eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  virtual bool ShouldSendUpdate(const content::Geoposition& position) const
61eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      OVERRIDE {
62eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    return !last_updated_position_.Validate() ||
63eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        Distance(position.latitude,
64eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                 position.longitude,
65eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                 last_updated_position_.latitude,
66eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                 last_updated_position_.longitude) >
67eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch            distance_update_threshold_meters_;
68eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  }
69eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
70eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  virtual void OnPositionReported(const content::Geoposition& position)
71eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      OVERRIDE {
72eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    last_updated_position_ = position;
73eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  }
74eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
75eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch private:
76eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  virtual ~DistanceBasedUpdatePolicy() {}
77eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
78eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // Calculates the distance between two latitude and longitude points.
79eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  static double Distance(const double latitude1,
80eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                         const double longitude1,
81eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                         const double latitude2,
82eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                         const double longitude2) {
83eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    // The earth has a radius of about 6371 km.
84eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    const double kRadius = 6371000;
85eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    const double kPi = 3.14159265358979323846;
86eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    const double kDegreesToRadians = kPi / 180.0;
87eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
88eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    // Conversions
89eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    const double latitude1Rad = latitude1 * kDegreesToRadians;
90eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    const double latitude2Rad = latitude2 * kDegreesToRadians;
91eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    const double latitudeDistRad = latitude2Rad - latitude1Rad;
92eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    const double longitudeDistRad = (longitude2 - longitude1) *
93eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                    kDegreesToRadians;
94eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
95eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    // The Haversine Formula determines the great circle distance
96eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    // between two points on a sphere.
97eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    const double chordLengthSquared = pow(sin(latitudeDistRad / 2.0), 2) +
98eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                      (pow(sin(longitudeDistRad / 2.0), 2) *
99eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                       cos(latitude1Rad) *
100eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                       cos(latitude2Rad));
101eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    const double angularDistance = 2.0 * atan2(sqrt(chordLengthSquared),
102eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                               sqrt(1.0 - chordLengthSquared));
103eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    return kRadius * angularDistance;
104eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  }
105eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
106eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  const double distance_update_threshold_meters_;
107eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  content::Geoposition last_updated_position_;
108eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
109eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  DISALLOW_COPY_AND_ASSIGN(DistanceBasedUpdatePolicy);
110eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch};
111eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
112eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// A policy that controls sending an update above a time threshold.
113eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochclass TimeBasedUpdatePolicy : public UpdatePolicy {
114eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch public:
115eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  explicit TimeBasedUpdatePolicy(double time_between_updates_ms) :
116eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      time_between_updates_ms_(time_between_updates_ms)
117eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  {}
118eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
119eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // UpdatePolicy Implementation
120eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  virtual bool ShouldSendUpdate(const content::Geoposition&) const OVERRIDE {
121eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    return (base::Time::Now() - last_update_time_).InMilliseconds() >
122eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        time_between_updates_ms_;
123eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  }
124eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
125eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  virtual void OnPositionReported(const content::Geoposition&) OVERRIDE {
126eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    last_update_time_ = base::Time::Now();
127eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  }
128eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
129eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch private:
130eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  virtual ~TimeBasedUpdatePolicy() {}
131eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
132eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  base::Time last_update_time_;
133eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  const double time_between_updates_ms_;
134eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
135eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  DISALLOW_COPY_AND_ASSIGN(TimeBasedUpdatePolicy);
136eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch};
137eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
138eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch} // namespace updatepolicy
139eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
140c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Request created by chrome.location.watchLocation() call.
141cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)class LocationRequest : public base::RefCounted<LocationRequest> {
142c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) public:
143c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  LocationRequest(
144cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      LocationManager* location_manager,
145c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      const std::string& extension_id,
146eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      const std::string& request_name,
147eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      const double* distance_update_threshold_meters,
148eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      const double* time_between_updates_ms);
149c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
150c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  const std::string& request_name() const { return request_name_; }
151c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
152c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) private:
153cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  friend class base::RefCounted<LocationRequest>;
154cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)   ~LocationRequest();
155c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
156b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  void OnLocationUpdate(const content::Geoposition& position);
157c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
158eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // Determines if all policies say to send a position update.
159eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // If there are no policies, this always says yes.
160eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  bool ShouldSendUpdate(const content::Geoposition& position);
161eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
162eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // Updates the policies on sending a position update.
163eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  void OnPositionReported(const content::Geoposition& position);
164eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
165c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Request name.
166c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  const std::string request_name_;
167c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
168c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Id of the owner extension.
169c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  const std::string extension_id_;
170c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
171c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Owning location manager.
172cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  LocationManager* location_manager_;
173c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
174eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // Holds Update Policies.
175eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  typedef std::vector<scoped_refptr<updatepolicy::UpdatePolicy> >
176eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      UpdatePolicyVector;
177eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  UpdatePolicyVector update_policies_;
178eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
179cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  scoped_ptr<content::GeolocationProvider::Subscription>
180cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      geolocation_subscription_;
181b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
182c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(LocationRequest);
183c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)};
184c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
185c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)LocationRequest::LocationRequest(
186cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    LocationManager* location_manager,
187c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const std::string& extension_id,
188eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    const std::string& request_name,
189eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    const double* distance_update_threshold_meters,
190eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    const double* time_between_updates_ms)
191c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    : request_name_(request_name),
192c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      extension_id_(extension_id),
193c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      location_manager_(location_manager) {
194c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // TODO(vadimt): use request_info.
195eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (time_between_updates_ms) {
196eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    update_policies_.push_back(
197eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        new updatepolicy::TimeBasedUpdatePolicy(
198eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch            *time_between_updates_ms));
199eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  }
200eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
201eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (distance_update_threshold_meters) {
202eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    update_policies_.push_back(
203eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        new updatepolicy::DistanceBasedUpdatePolicy(
204eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch              *distance_update_threshold_meters));
205eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  }
206c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
207c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // TODO(vadimt): This can get a location cached by GeolocationProvider,
208c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // contrary to the API definition which says that creating a location watch
209c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // will get new location.
210cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  geolocation_subscription_ = content::GeolocationProvider::GetInstance()->
211cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      AddLocationUpdateCallback(
212cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)          base::Bind(&LocationRequest::OnLocationUpdate,
213cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                     base::Unretained(this)),
214cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)          true);
215cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
216cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
217cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)LocationRequest::~LocationRequest() {
218c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
219c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
220c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void LocationRequest::OnLocationUpdate(const content::Geoposition& position) {
221eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (ShouldSendUpdate(position)) {
222eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    OnPositionReported(position);
223cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    location_manager_->SendLocationUpdate(
224cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        extension_id_, request_name_, position);
225eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  }
226eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
227c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
228eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochbool LocationRequest::ShouldSendUpdate(const content::Geoposition& position) {
229eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  for (UpdatePolicyVector::iterator it = update_policies_.begin();
230eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch       it != update_policies_.end();
231eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch       ++it) {
232eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    if (!((*it)->ShouldSendUpdate(position))) {
233eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      return false;
234eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    }
235eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  }
236eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  return true;
237eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
238eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
239eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid LocationRequest::OnPositionReported(const content::Geoposition& position) {
240eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  for (UpdatePolicyVector::iterator it = update_policies_.begin();
241eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch       it != update_policies_.end();
242eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch       ++it) {
243eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    (*it)->OnPositionReported(position);
244eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  }
245c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
246c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
247a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)LocationManager::LocationManager(content::BrowserContext* context)
2486d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    : browser_context_(context), extension_registry_observer_(this) {
2496d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_));
250c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
251c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
252eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid LocationManager::AddLocationRequest(
253eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    const std::string& extension_id,
254eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    const std::string& request_name,
255eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    const double* distance_update_threshold_meters,
256eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    const double* time_between_updates_ms) {
257effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  DCHECK_CURRENTLY_ON(BrowserThread::UI);
258c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // TODO(vadimt): Consider resuming requests after restarting the browser.
259c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
260c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Override any old request with the same name.
26190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  RemoveLocationRequest(extension_id, request_name);
262c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
263eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  LocationRequestPointer location_request =
264cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      new LocationRequest(this,
265eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                          extension_id,
266eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                          request_name,
267eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                          distance_update_threshold_meters,
268eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                          time_between_updates_ms);
26990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  location_requests_.insert(
27090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      LocationRequestMap::value_type(extension_id, location_request));
271c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
272c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
273c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void LocationManager::RemoveLocationRequest(const std::string& extension_id,
274c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                            const std::string& name) {
275effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  DCHECK_CURRENTLY_ON(BrowserThread::UI);
276c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
27790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  std::pair<LocationRequestMap::iterator, LocationRequestMap::iterator>
27890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      extension_range = location_requests_.equal_range(extension_id);
279c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
28090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  for (LocationRequestMap::iterator it = extension_range.first;
28190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)       it != extension_range.second;
28290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)       ++it) {
28390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    if (it->second->request_name() == name) {
28490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      location_requests_.erase(it);
28590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      return;
28690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    }
28790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  }
288c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
289c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
290c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)LocationManager::~LocationManager() {
291c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
292c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
293c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void LocationManager::GeopositionToApiCoordinates(
294c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      const content::Geoposition& position,
295c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      location::Coordinates* coordinates) {
296c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  coordinates->latitude = position.latitude;
297c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  coordinates->longitude = position.longitude;
298c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (position.altitude > -10000.)
299c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    coordinates->altitude.reset(new double(position.altitude));
300c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  coordinates->accuracy = position.accuracy;
301c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (position.altitude_accuracy >= 0.) {
302c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    coordinates->altitude_accuracy.reset(
303c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        new double(position.altitude_accuracy));
304c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
305c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (position.heading >= 0. && position.heading <= 360.)
306c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    coordinates->heading.reset(new double(position.heading));
307c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (position.speed >= 0.)
308c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    coordinates->speed.reset(new double(position.speed));
309c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
310c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
311c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void LocationManager::SendLocationUpdate(
312c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const std::string& extension_id,
313c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const std::string& request_name,
314c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const content::Geoposition& position) {
315effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  DCHECK_CURRENTLY_ON(BrowserThread::UI);
316c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
317eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  scoped_ptr<base::ListValue> args(new base::ListValue());
318c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::string event_name;
319c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
320c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (position.Validate() &&
321c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      position.error_code == content::Geoposition::ERROR_CODE_NONE) {
322c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // Set data for onLocationUpdate event.
323c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    location::Location location;
324c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    location.name = request_name;
325c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    GeopositionToApiCoordinates(position, &location.coords);
326c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    location.timestamp = position.timestamp.ToJsTime();
327c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
328c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    args->Append(location.ToValue().release());
3293551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    event_name = location::OnLocationUpdate::kEventName;
330c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  } else {
331c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // Set data for onLocationError event.
332c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // TODO(vadimt): Set name.
333c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    args->AppendString(position.error_message);
3343551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    event_name = location::OnLocationError::kEventName;
335c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
336c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
337c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  scoped_ptr<Event> event(new Event(event_name, args.Pass()));
338c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
3396d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  EventRouter::Get(browser_context_)
3400529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch      ->DispatchEventToExtension(extension_id, event.Pass());
341c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
342c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
3436d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)void LocationManager::OnExtensionLoaded(
3446d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    content::BrowserContext* browser_context,
3456d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    const Extension* extension) {
3466d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // Grants permission to use geolocation once an extension with "location"
3476d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // permission is loaded.
3486d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  if (extension->permissions_data()->HasAPIPermission(
3496d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)          APIPermission::kLocation)) {
3506d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    content::GeolocationProvider::GetInstance()
3516d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)        ->UserDidOptIntoLocationServices();
352c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
353c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
354c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
3556d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)void LocationManager::OnExtensionUnloaded(
3566d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    content::BrowserContext* browser_context,
3576d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    const Extension* extension,
3586d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    UnloadedExtensionInfo::Reason reason) {
3596d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // Delete all requests from the unloaded extension.
3606d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  location_requests_.erase(extension->id());
3616d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)}
3626d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
363a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)static base::LazyInstance<BrowserContextKeyedAPIFactory<LocationManager> >
364a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    g_factory = LAZY_INSTANCE_INITIALIZER;
365a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)
366a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)// static
367a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)BrowserContextKeyedAPIFactory<LocationManager>*
368a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)LocationManager::GetFactoryInstance() {
3695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return g_factory.Pointer();
370a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)}
371a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)
3726d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)// static
373a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)LocationManager* LocationManager::Get(content::BrowserContext* context) {
374a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return BrowserContextKeyedAPIFactory<LocationManager>::Get(context);
375a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)}
376a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)
377c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}  // namespace extensions
378