Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / api / location / location_manager.cc
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.
4
5 #include "chrome/browser/extensions/api/location/location_manager.h"
6
7 #include <math.h>
8 #include <vector>
9
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"
23
24 using content::BrowserThread;
25
26 // TODO(vadimt): Add tests.
27 namespace extensions {
28
29 namespace location = api::location;
30
31 namespace updatepolicy {
32
33 // Base class for all update policies for sending a location.
34 class UpdatePolicy : public base::RefCounted<UpdatePolicy> {
35  public:
36   explicit UpdatePolicy() {}
37
38   // True if the caller should send an update based off of this policy.
39   virtual bool ShouldSendUpdate(const content::Geoposition&) const = 0;
40
41   // Updates any policy state on reporting a position.
42   virtual void OnPositionReported(const content::Geoposition&) = 0;
43
44  protected:
45   virtual ~UpdatePolicy() {}
46
47  private:
48   friend class base::RefCounted<UpdatePolicy>;
49   DISALLOW_COPY_AND_ASSIGN(UpdatePolicy);
50 };
51
52 // A policy that controls sending an update below a distance threshold.
53 class DistanceBasedUpdatePolicy : public UpdatePolicy {
54  public:
55   explicit DistanceBasedUpdatePolicy(double distance_update_threshold_meters) :
56       distance_update_threshold_meters_(distance_update_threshold_meters)
57   {}
58
59   // UpdatePolicy Implementation
60   bool ShouldSendUpdate(const content::Geoposition& position) const override {
61     return !last_updated_position_.Validate() ||
62         Distance(position.latitude,
63                  position.longitude,
64                  last_updated_position_.latitude,
65                  last_updated_position_.longitude) >
66             distance_update_threshold_meters_;
67   }
68
69   void OnPositionReported(const content::Geoposition& position) override {
70     last_updated_position_ = position;
71   }
72
73  private:
74   ~DistanceBasedUpdatePolicy() override {}
75
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;
85
86     // Conversions
87     const double latitude1Rad = latitude1 * kDegreesToRadians;
88     const double latitude2Rad = latitude2 * kDegreesToRadians;
89     const double latitudeDistRad = latitude2Rad - latitude1Rad;
90     const double longitudeDistRad = (longitude2 - longitude1) *
91                                     kDegreesToRadians;
92
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) *
97                                        cos(latitude1Rad) *
98                                        cos(latitude2Rad));
99     const double angularDistance = 2.0 * atan2(sqrt(chordLengthSquared),
100                                                sqrt(1.0 - chordLengthSquared));
101     return kRadius * angularDistance;
102   }
103
104   const double distance_update_threshold_meters_;
105   content::Geoposition last_updated_position_;
106
107   DISALLOW_COPY_AND_ASSIGN(DistanceBasedUpdatePolicy);
108 };
109
110 // A policy that controls sending an update above a time threshold.
111 class TimeBasedUpdatePolicy : public UpdatePolicy {
112  public:
113   explicit TimeBasedUpdatePolicy(double time_between_updates_ms) :
114       time_between_updates_ms_(time_between_updates_ms)
115   {}
116
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_;
121   }
122
123   void OnPositionReported(const content::Geoposition&) override {
124     last_update_time_ = base::Time::Now();
125   }
126
127  private:
128   ~TimeBasedUpdatePolicy() override {}
129
130   base::Time last_update_time_;
131   const double time_between_updates_ms_;
132
133   DISALLOW_COPY_AND_ASSIGN(TimeBasedUpdatePolicy);
134 };
135
136 } // namespace updatepolicy
137
138 // Request created by chrome.location.watchLocation() call.
139 class LocationRequest : public base::RefCounted<LocationRequest> {
140  public:
141   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);
147
148   const std::string& request_name() const { return request_name_; }
149
150  private:
151   friend class base::RefCounted<LocationRequest>;
152    ~LocationRequest();
153
154   void OnLocationUpdate(const content::Geoposition& position);
155
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);
159
160   // Updates the policies on sending a position update.
161   void OnPositionReported(const content::Geoposition& position);
162
163   // Request name.
164   const std::string request_name_;
165
166   // Id of the owner extension.
167   const std::string extension_id_;
168
169   // Owning location manager.
170   LocationManager* location_manager_;
171
172   // Holds Update Policies.
173   typedef std::vector<scoped_refptr<updatepolicy::UpdatePolicy> >
174       UpdatePolicyVector;
175   UpdatePolicyVector update_policies_;
176
177   scoped_ptr<content::GeolocationProvider::Subscription>
178       geolocation_subscription_;
179
180   DISALLOW_COPY_AND_ASSIGN(LocationRequest);
181 };
182
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));
197   }
198
199   if (distance_update_threshold_meters) {
200     update_policies_.push_back(
201         new updatepolicy::DistanceBasedUpdatePolicy(
202               *distance_update_threshold_meters));
203   }
204
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)),
212           true);
213 }
214
215 LocationRequest::~LocationRequest() {
216 }
217
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);
223   }
224 }
225
226 bool LocationRequest::ShouldSendUpdate(const content::Geoposition& position) {
227   for (UpdatePolicyVector::iterator it = update_policies_.begin();
228        it != update_policies_.end();
229        ++it) {
230     if (!((*it)->ShouldSendUpdate(position))) {
231       return false;
232     }
233   }
234   return true;
235 }
236
237 void LocationRequest::OnPositionReported(const content::Geoposition& position) {
238   for (UpdatePolicyVector::iterator it = update_policies_.begin();
239        it != update_policies_.end();
240        ++it) {
241     (*it)->OnPositionReported(position);
242   }
243 }
244
245 LocationManager::LocationManager(content::BrowserContext* context)
246     : browser_context_(context), extension_registry_observer_(this) {
247   extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_));
248 }
249
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.
257
258   // Override any old request with the same name.
259   RemoveLocationRequest(extension_id, request_name);
260
261   LocationRequestPointer location_request =
262       new LocationRequest(this,
263                           extension_id,
264                           request_name,
265                           distance_update_threshold_meters,
266                           time_between_updates_ms);
267   location_requests_.insert(
268       LocationRequestMap::value_type(extension_id, location_request));
269 }
270
271 void LocationManager::RemoveLocationRequest(const std::string& extension_id,
272                                             const std::string& name) {
273   DCHECK_CURRENTLY_ON(BrowserThread::UI);
274
275   std::pair<LocationRequestMap::iterator, LocationRequestMap::iterator>
276       extension_range = location_requests_.equal_range(extension_id);
277
278   for (LocationRequestMap::iterator it = extension_range.first;
279        it != extension_range.second;
280        ++it) {
281     if (it->second->request_name() == name) {
282       location_requests_.erase(it);
283       return;
284     }
285   }
286 }
287
288 LocationManager::~LocationManager() {
289 }
290
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));
302   }
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));
307 }
308
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);
314
315   scoped_ptr<base::ListValue> args(new base::ListValue());
316   std::string event_name;
317
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();
325
326     args->Append(location.ToValue().release());
327     event_name = location::OnLocationUpdate::kEventName;
328   } else {
329     // Set data for onLocationError event.
330     // TODO(vadimt): Set name.
331     args->AppendString(position.error_message);
332     event_name = location::OnLocationError::kEventName;
333   }
334
335   scoped_ptr<Event> event(new Event(event_name, args.Pass()));
336
337   EventRouter::Get(browser_context_)
338       ->DispatchEventToExtension(extension_id, event.Pass());
339 }
340
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();
350   }
351 }
352
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());
359 }
360
361 static base::LazyInstance<BrowserContextKeyedAPIFactory<LocationManager> >
362     g_factory = LAZY_INSTANCE_INITIALIZER;
363
364 // static
365 BrowserContextKeyedAPIFactory<LocationManager>*
366 LocationManager::GetFactoryInstance() {
367   return g_factory.Pointer();
368 }
369
370 // static
371 LocationManager* LocationManager::Get(content::BrowserContext* context) {
372   return BrowserContextKeyedAPIFactory<LocationManager>::Get(context);
373 }
374
375 }  // namespace extensions