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 virtual bool ShouldSendUpdate(const content::Geoposition& position) const 61 OVERRIDE { 62 return !last_updated_position_.Validate() || 63 Distance(position.latitude, 64 position.longitude, 65 last_updated_position_.latitude, 66 last_updated_position_.longitude) > 67 distance_update_threshold_meters_; 68 } 69 70 virtual void OnPositionReported(const content::Geoposition& position) 71 OVERRIDE { 72 last_updated_position_ = position; 73 } 74 75 private: 76 virtual ~DistanceBasedUpdatePolicy() {} 77 78 // Calculates the distance between two latitude and longitude points. 79 static double Distance(const double latitude1, 80 const double longitude1, 81 const double latitude2, 82 const double longitude2) { 83 // The earth has a radius of about 6371 km. 84 const double kRadius = 6371000; 85 const double kPi = 3.14159265358979323846; 86 const double kDegreesToRadians = kPi / 180.0; 87 88 // Conversions 89 const double latitude1Rad = latitude1 * kDegreesToRadians; 90 const double latitude2Rad = latitude2 * kDegreesToRadians; 91 const double latitudeDistRad = latitude2Rad - latitude1Rad; 92 const double longitudeDistRad = (longitude2 - longitude1) * 93 kDegreesToRadians; 94 95 // The Haversine Formula determines the great circle distance 96 // between two points on a sphere. 97 const double chordLengthSquared = pow(sin(latitudeDistRad / 2.0), 2) + 98 (pow(sin(longitudeDistRad / 2.0), 2) * 99 cos(latitude1Rad) * 100 cos(latitude2Rad)); 101 const double angularDistance = 2.0 * atan2(sqrt(chordLengthSquared), 102 sqrt(1.0 - chordLengthSquared)); 103 return kRadius * angularDistance; 104 } 105 106 const double distance_update_threshold_meters_; 107 content::Geoposition last_updated_position_; 108 109 DISALLOW_COPY_AND_ASSIGN(DistanceBasedUpdatePolicy); 110 }; 111 112 // A policy that controls sending an update above a time threshold. 113 class TimeBasedUpdatePolicy : public UpdatePolicy { 114 public: 115 explicit TimeBasedUpdatePolicy(double time_between_updates_ms) : 116 time_between_updates_ms_(time_between_updates_ms) 117 {} 118 119 // UpdatePolicy Implementation 120 virtual bool ShouldSendUpdate(const content::Geoposition&) const OVERRIDE { 121 return (base::Time::Now() - last_update_time_).InMilliseconds() > 122 time_between_updates_ms_; 123 } 124 125 virtual void OnPositionReported(const content::Geoposition&) OVERRIDE { 126 last_update_time_ = base::Time::Now(); 127 } 128 129 private: 130 virtual ~TimeBasedUpdatePolicy() {} 131 132 base::Time last_update_time_; 133 const double time_between_updates_ms_; 134 135 DISALLOW_COPY_AND_ASSIGN(TimeBasedUpdatePolicy); 136 }; 137 138 } // namespace updatepolicy 139 140 // Request created by chrome.location.watchLocation() call. 141 class LocationRequest : public base::RefCounted<LocationRequest> { 142 public: 143 LocationRequest( 144 LocationManager* location_manager, 145 const std::string& extension_id, 146 const std::string& request_name, 147 const double* distance_update_threshold_meters, 148 const double* time_between_updates_ms); 149 150 const std::string& request_name() const { return request_name_; } 151 152 private: 153 friend class base::RefCounted<LocationRequest>; 154 ~LocationRequest(); 155 156 void OnLocationUpdate(const content::Geoposition& position); 157 158 // Determines if all policies say to send a position update. 159 // If there are no policies, this always says yes. 160 bool ShouldSendUpdate(const content::Geoposition& position); 161 162 // Updates the policies on sending a position update. 163 void OnPositionReported(const content::Geoposition& position); 164 165 // Request name. 166 const std::string request_name_; 167 168 // Id of the owner extension. 169 const std::string extension_id_; 170 171 // Owning location manager. 172 LocationManager* location_manager_; 173 174 // Holds Update Policies. 175 typedef std::vector<scoped_refptr<updatepolicy::UpdatePolicy> > 176 UpdatePolicyVector; 177 UpdatePolicyVector update_policies_; 178 179 scoped_ptr<content::GeolocationProvider::Subscription> 180 geolocation_subscription_; 181 182 DISALLOW_COPY_AND_ASSIGN(LocationRequest); 183 }; 184 185 LocationRequest::LocationRequest( 186 LocationManager* location_manager, 187 const std::string& extension_id, 188 const std::string& request_name, 189 const double* distance_update_threshold_meters, 190 const double* time_between_updates_ms) 191 : request_name_(request_name), 192 extension_id_(extension_id), 193 location_manager_(location_manager) { 194 // TODO(vadimt): use request_info. 195 if (time_between_updates_ms) { 196 update_policies_.push_back( 197 new updatepolicy::TimeBasedUpdatePolicy( 198 *time_between_updates_ms)); 199 } 200 201 if (distance_update_threshold_meters) { 202 update_policies_.push_back( 203 new updatepolicy::DistanceBasedUpdatePolicy( 204 *distance_update_threshold_meters)); 205 } 206 207 // TODO(vadimt): This can get a location cached by GeolocationProvider, 208 // contrary to the API definition which says that creating a location watch 209 // will get new location. 210 geolocation_subscription_ = content::GeolocationProvider::GetInstance()-> 211 AddLocationUpdateCallback( 212 base::Bind(&LocationRequest::OnLocationUpdate, 213 base::Unretained(this)), 214 true); 215 } 216 217 LocationRequest::~LocationRequest() { 218 } 219 220 void LocationRequest::OnLocationUpdate(const content::Geoposition& position) { 221 if (ShouldSendUpdate(position)) { 222 OnPositionReported(position); 223 location_manager_->SendLocationUpdate( 224 extension_id_, request_name_, position); 225 } 226 } 227 228 bool LocationRequest::ShouldSendUpdate(const content::Geoposition& position) { 229 for (UpdatePolicyVector::iterator it = update_policies_.begin(); 230 it != update_policies_.end(); 231 ++it) { 232 if (!((*it)->ShouldSendUpdate(position))) { 233 return false; 234 } 235 } 236 return true; 237 } 238 239 void LocationRequest::OnPositionReported(const content::Geoposition& position) { 240 for (UpdatePolicyVector::iterator it = update_policies_.begin(); 241 it != update_policies_.end(); 242 ++it) { 243 (*it)->OnPositionReported(position); 244 } 245 } 246 247 LocationManager::LocationManager(content::BrowserContext* context) 248 : browser_context_(context), extension_registry_observer_(this) { 249 extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_)); 250 } 251 252 void LocationManager::AddLocationRequest( 253 const std::string& extension_id, 254 const std::string& request_name, 255 const double* distance_update_threshold_meters, 256 const double* time_between_updates_ms) { 257 DCHECK_CURRENTLY_ON(BrowserThread::UI); 258 // TODO(vadimt): Consider resuming requests after restarting the browser. 259 260 // Override any old request with the same name. 261 RemoveLocationRequest(extension_id, request_name); 262 263 LocationRequestPointer location_request = 264 new LocationRequest(this, 265 extension_id, 266 request_name, 267 distance_update_threshold_meters, 268 time_between_updates_ms); 269 location_requests_.insert( 270 LocationRequestMap::value_type(extension_id, location_request)); 271 } 272 273 void LocationManager::RemoveLocationRequest(const std::string& extension_id, 274 const std::string& name) { 275 DCHECK_CURRENTLY_ON(BrowserThread::UI); 276 277 std::pair<LocationRequestMap::iterator, LocationRequestMap::iterator> 278 extension_range = location_requests_.equal_range(extension_id); 279 280 for (LocationRequestMap::iterator it = extension_range.first; 281 it != extension_range.second; 282 ++it) { 283 if (it->second->request_name() == name) { 284 location_requests_.erase(it); 285 return; 286 } 287 } 288 } 289 290 LocationManager::~LocationManager() { 291 } 292 293 void LocationManager::GeopositionToApiCoordinates( 294 const content::Geoposition& position, 295 location::Coordinates* coordinates) { 296 coordinates->latitude = position.latitude; 297 coordinates->longitude = position.longitude; 298 if (position.altitude > -10000.) 299 coordinates->altitude.reset(new double(position.altitude)); 300 coordinates->accuracy = position.accuracy; 301 if (position.altitude_accuracy >= 0.) { 302 coordinates->altitude_accuracy.reset( 303 new double(position.altitude_accuracy)); 304 } 305 if (position.heading >= 0. && position.heading <= 360.) 306 coordinates->heading.reset(new double(position.heading)); 307 if (position.speed >= 0.) 308 coordinates->speed.reset(new double(position.speed)); 309 } 310 311 void LocationManager::SendLocationUpdate( 312 const std::string& extension_id, 313 const std::string& request_name, 314 const content::Geoposition& position) { 315 DCHECK_CURRENTLY_ON(BrowserThread::UI); 316 317 scoped_ptr<base::ListValue> args(new base::ListValue()); 318 std::string event_name; 319 320 if (position.Validate() && 321 position.error_code == content::Geoposition::ERROR_CODE_NONE) { 322 // Set data for onLocationUpdate event. 323 location::Location location; 324 location.name = request_name; 325 GeopositionToApiCoordinates(position, &location.coords); 326 location.timestamp = position.timestamp.ToJsTime(); 327 328 args->Append(location.ToValue().release()); 329 event_name = location::OnLocationUpdate::kEventName; 330 } else { 331 // Set data for onLocationError event. 332 // TODO(vadimt): Set name. 333 args->AppendString(position.error_message); 334 event_name = location::OnLocationError::kEventName; 335 } 336 337 scoped_ptr<Event> event(new Event(event_name, args.Pass())); 338 339 EventRouter::Get(browser_context_) 340 ->DispatchEventToExtension(extension_id, event.Pass()); 341 } 342 343 void LocationManager::OnExtensionLoaded( 344 content::BrowserContext* browser_context, 345 const Extension* extension) { 346 // Grants permission to use geolocation once an extension with "location" 347 // permission is loaded. 348 if (extension->permissions_data()->HasAPIPermission( 349 APIPermission::kLocation)) { 350 content::GeolocationProvider::GetInstance() 351 ->UserDidOptIntoLocationServices(); 352 } 353 } 354 355 void LocationManager::OnExtensionUnloaded( 356 content::BrowserContext* browser_context, 357 const Extension* extension, 358 UnloadedExtensionInfo::Reason reason) { 359 // Delete all requests from the unloaded extension. 360 location_requests_.erase(extension->id()); 361 } 362 363 static base::LazyInstance<BrowserContextKeyedAPIFactory<LocationManager> > 364 g_factory = LAZY_INSTANCE_INITIALIZER; 365 366 // static 367 BrowserContextKeyedAPIFactory<LocationManager>* 368 LocationManager::GetFactoryInstance() { 369 return g_factory.Pointer(); 370 } 371 372 // static 373 LocationManager* LocationManager::Get(content::BrowserContext* context) { 374 return BrowserContextKeyedAPIFactory<LocationManager>::Get(context); 375 } 376 377 } // namespace extensions 378