This source file includes following definitions.
- ShouldSendUpdate
- OnPositionReported
- Distance
- ShouldSendUpdate
- OnPositionReported
- request_name
- Initialize
- GrantPermission
- AddObserverOnIOThread
- OnLocationUpdate
- ShouldSendUpdate
- OnPositionReported
- AddLocationRequest
- RemoveLocationRequest
- GeopositionToApiCoordinates
- SendLocationUpdate
- Observe
- GetFactoryInstance
- Get
#include "chrome/browser/extensions/api/location/location_manager.h"
#include <math.h>
#include <vector>
#include "base/bind.h"
#include "base/lazy_instance.h"
#include "base/time/time.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/extensions/api/location.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/geolocation_provider.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_source.h"
#include "content/public/common/geoposition.h"
#include "extensions/browser/event_router.h"
#include "extensions/browser/extension_system.h"
#include "extensions/common/extension.h"
#include "extensions/common/permissions/permission_set.h"
using content::BrowserThread;
namespace extensions {
namespace location = api::location;
namespace updatepolicy {
class UpdatePolicy : public base::RefCounted<UpdatePolicy> {
public:
explicit UpdatePolicy() {}
virtual bool ShouldSendUpdate(const content::Geoposition&) const = 0;
virtual void OnPositionReported(const content::Geoposition&) = 0;
protected:
virtual ~UpdatePolicy() {}
private:
friend class base::RefCounted<UpdatePolicy>;
DISALLOW_COPY_AND_ASSIGN(UpdatePolicy);
};
class DistanceBasedUpdatePolicy : public UpdatePolicy {
public:
explicit DistanceBasedUpdatePolicy(double distance_update_threshold_meters) :
distance_update_threshold_meters_(distance_update_threshold_meters)
{}
virtual bool ShouldSendUpdate(const content::Geoposition& position) const
OVERRIDE {
return !last_updated_position_.Validate() ||
Distance(position.latitude,
position.longitude,
last_updated_position_.latitude,
last_updated_position_.longitude) >
distance_update_threshold_meters_;
}
virtual void OnPositionReported(const content::Geoposition& position)
OVERRIDE {
last_updated_position_ = position;
}
private:
virtual ~DistanceBasedUpdatePolicy() {}
static double Distance(const double latitude1,
const double longitude1,
const double latitude2,
const double longitude2) {
const double kRadius = 6371000;
const double kPi = 3.14159265358979323846;
const double kDegreesToRadians = kPi / 180.0;
const double latitude1Rad = latitude1 * kDegreesToRadians;
const double latitude2Rad = latitude2 * kDegreesToRadians;
const double latitudeDistRad = latitude2Rad - latitude1Rad;
const double longitudeDistRad = (longitude2 - longitude1) *
kDegreesToRadians;
const double chordLengthSquared = pow(sin(latitudeDistRad / 2.0), 2) +
(pow(sin(longitudeDistRad / 2.0), 2) *
cos(latitude1Rad) *
cos(latitude2Rad));
const double angularDistance = 2.0 * atan2(sqrt(chordLengthSquared),
sqrt(1.0 - chordLengthSquared));
return kRadius * angularDistance;
}
const double distance_update_threshold_meters_;
content::Geoposition last_updated_position_;
DISALLOW_COPY_AND_ASSIGN(DistanceBasedUpdatePolicy);
};
class TimeBasedUpdatePolicy : public UpdatePolicy {
public:
explicit TimeBasedUpdatePolicy(double time_between_updates_ms) :
time_between_updates_ms_(time_between_updates_ms)
{}
virtual bool ShouldSendUpdate(const content::Geoposition&) const OVERRIDE {
return (base::Time::Now() - last_update_time_).InMilliseconds() >
time_between_updates_ms_;
}
virtual void OnPositionReported(const content::Geoposition&) OVERRIDE {
last_update_time_ = base::Time::Now();
}
private:
virtual ~TimeBasedUpdatePolicy() {}
base::Time last_update_time_;
const double time_between_updates_ms_;
DISALLOW_COPY_AND_ASSIGN(TimeBasedUpdatePolicy);
};
}
class LocationRequest
: public base::RefCountedThreadSafe<LocationRequest,
BrowserThread::DeleteOnIOThread> {
public:
LocationRequest(
const base::WeakPtr<LocationManager>& location_manager,
const std::string& extension_id,
const std::string& request_name,
const double* distance_update_threshold_meters,
const double* time_between_updates_ms);
void Initialize();
const std::string& request_name() const { return request_name_; }
static void GrantPermission();
private:
friend class base::DeleteHelper<LocationRequest>;
friend struct BrowserThread::DeleteOnThread<BrowserThread::IO>;
virtual ~LocationRequest();
void AddObserverOnIOThread();
void OnLocationUpdate(const content::Geoposition& position);
bool ShouldSendUpdate(const content::Geoposition& position);
void OnPositionReported(const content::Geoposition& position);
const std::string request_name_;
const std::string extension_id_;
const base::WeakPtr<LocationManager> location_manager_;
typedef std::vector<scoped_refptr<updatepolicy::UpdatePolicy> >
UpdatePolicyVector;
UpdatePolicyVector update_policies_;
content::GeolocationProvider::LocationUpdateCallback callback_;
DISALLOW_COPY_AND_ASSIGN(LocationRequest);
};
LocationRequest::LocationRequest(
const base::WeakPtr<LocationManager>& location_manager,
const std::string& extension_id,
const std::string& request_name,
const double* distance_update_threshold_meters,
const double* time_between_updates_ms)
: request_name_(request_name),
extension_id_(extension_id),
location_manager_(location_manager) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (time_between_updates_ms) {
update_policies_.push_back(
new updatepolicy::TimeBasedUpdatePolicy(
*time_between_updates_ms));
}
if (distance_update_threshold_meters) {
update_policies_.push_back(
new updatepolicy::DistanceBasedUpdatePolicy(
*distance_update_threshold_meters));
}
}
void LocationRequest::Initialize() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
callback_ = base::Bind(&LocationRequest::OnLocationUpdate,
base::Unretained(this));
BrowserThread::PostTask(
BrowserThread::IO,
FROM_HERE,
base::Bind(&LocationRequest::AddObserverOnIOThread,
this));
}
void LocationRequest::GrantPermission() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
content::GeolocationProvider::GetInstance()->UserDidOptIntoLocationServices();
}
LocationRequest::~LocationRequest() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
content::GeolocationProvider::GetInstance()->RemoveLocationUpdateCallback(
callback_);
}
void LocationRequest::AddObserverOnIOThread() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
content::GeolocationProvider::GetInstance()->AddLocationUpdateCallback(
callback_, true);
}
void LocationRequest::OnLocationUpdate(const content::Geoposition& position) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (ShouldSendUpdate(position)) {
OnPositionReported(position);
BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
base::Bind(&LocationManager::SendLocationUpdate,
location_manager_,
extension_id_,
request_name_,
position));
}
}
bool LocationRequest::ShouldSendUpdate(const content::Geoposition& position) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
for (UpdatePolicyVector::iterator it = update_policies_.begin();
it != update_policies_.end();
++it) {
if (!((*it)->ShouldSendUpdate(position))) {
return false;
}
}
return true;
}
void LocationRequest::OnPositionReported(const content::Geoposition& position) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
for (UpdatePolicyVector::iterator it = update_policies_.begin();
it != update_policies_.end();
++it) {
(*it)->OnPositionReported(position);
}
}
LocationManager::LocationManager(content::BrowserContext* context)
: profile_(Profile::FromBrowserContext(context)) {
registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
content::Source<Profile>(profile_));
registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED,
content::Source<Profile>(profile_));
}
void LocationManager::AddLocationRequest(
const std::string& extension_id,
const std::string& request_name,
const double* distance_update_threshold_meters,
const double* time_between_updates_ms) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
RemoveLocationRequest(extension_id, request_name);
LocationRequestPointer location_request =
new LocationRequest(AsWeakPtr(),
extension_id,
request_name,
distance_update_threshold_meters,
time_between_updates_ms);
location_request->Initialize();
location_requests_.insert(
LocationRequestMap::value_type(extension_id, location_request));
}
void LocationManager::RemoveLocationRequest(const std::string& extension_id,
const std::string& name) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
std::pair<LocationRequestMap::iterator, LocationRequestMap::iterator>
extension_range = location_requests_.equal_range(extension_id);
for (LocationRequestMap::iterator it = extension_range.first;
it != extension_range.second;
++it) {
if (it->second->request_name() == name) {
location_requests_.erase(it);
return;
}
}
}
LocationManager::~LocationManager() {
}
void LocationManager::GeopositionToApiCoordinates(
const content::Geoposition& position,
location::Coordinates* coordinates) {
coordinates->latitude = position.latitude;
coordinates->longitude = position.longitude;
if (position.altitude > -10000.)
coordinates->altitude.reset(new double(position.altitude));
coordinates->accuracy = position.accuracy;
if (position.altitude_accuracy >= 0.) {
coordinates->altitude_accuracy.reset(
new double(position.altitude_accuracy));
}
if (position.heading >= 0. && position.heading <= 360.)
coordinates->heading.reset(new double(position.heading));
if (position.speed >= 0.)
coordinates->speed.reset(new double(position.speed));
}
void LocationManager::SendLocationUpdate(
const std::string& extension_id,
const std::string& request_name,
const content::Geoposition& position) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
scoped_ptr<base::ListValue> args(new base::ListValue());
std::string event_name;
if (position.Validate() &&
position.error_code == content::Geoposition::ERROR_CODE_NONE) {
location::Location location;
location.name = request_name;
GeopositionToApiCoordinates(position, &location.coords);
location.timestamp = position.timestamp.ToJsTime();
args->Append(location.ToValue().release());
event_name = location::OnLocationUpdate::kEventName;
} else {
args->AppendString(position.error_message);
event_name = location::OnLocationError::kEventName;
}
scoped_ptr<Event> event(new Event(event_name, args.Pass()));
ExtensionSystem::Get(profile_)->event_router()->
DispatchEventToExtension(extension_id, event.Pass());
}
void LocationManager::Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
switch (type) {
case chrome::NOTIFICATION_EXTENSION_LOADED: {
const Extension* extension =
content::Details<const Extension>(details).ptr();
if (extension->HasAPIPermission(APIPermission::kLocation)) {
BrowserThread::PostTask(
BrowserThread::IO,
FROM_HERE,
base::Bind(&LocationRequest::GrantPermission));
}
break;
}
case chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED: {
const Extension* extension =
content::Details<const UnloadedExtensionInfo>(details)->extension;
location_requests_.erase(extension->id());
break;
}
default:
NOTREACHED();
break;
}
}
static base::LazyInstance<BrowserContextKeyedAPIFactory<LocationManager> >
g_factory = LAZY_INSTANCE_INITIALIZER;
BrowserContextKeyedAPIFactory<LocationManager>*
LocationManager::GetFactoryInstance() {
return g_factory.Pointer();
}
LocationManager* LocationManager::Get(content::BrowserContext* context) {
return BrowserContextKeyedAPIFactory<LocationManager>::Get(context);
}
}