Index: chrome/browser/policy/device_status_collector.cc |
diff --git a/chrome/browser/policy/device_status_collector.cc b/chrome/browser/policy/device_status_collector.cc |
index 5a1be4a47e71c915d8cacb835f309389be5cc942..f7dc3b640f7f07047d45405b68ee45194e1e9e84 100644 |
--- a/chrome/browser/policy/device_status_collector.cc |
+++ b/chrome/browser/policy/device_status_collector.cc |
@@ -16,6 +16,8 @@ |
#include "chrome/common/chrome_notification_types.h" |
#include "chrome/common/chrome_version_info.h" |
#include "chrome/common/pref_names.h" |
+#include "content/browser/geolocation/geolocation_provider.h" |
+#include "content/public/browser/browser_thread.h" |
using base::Time; |
using base::TimeDelta; |
@@ -53,6 +55,210 @@ void AddDeviceActivity(DictionaryValue* activity_times, |
namespace policy { |
+// Helper class for retrieving the device's geographic location. |
+class DeviceStatusLocationHelper : public GeolocationObserver { |
Joao da Silva
2012/04/20 09:49:57
I'd move this one to its own file.
bartfab (slow)
2012/04/20 14:10:15
Done.
|
+ public: |
+ explicit DeviceStatusLocationHelper(PrefService* local_state); |
+ virtual ~DeviceStatusLocationHelper(); |
+ void Destruct(); |
+ |
+ const Geoposition& GetPosition() const; |
+ |
+ static void RegisterPrefs(PrefService* local_state); |
+ |
+ virtual void OnLocationUpdate(const Geoposition& position); |
Joao da Silva
2012/04/20 09:49:57
OVERRIDE
bartfab (slow)
2012/04/20 14:10:15
Done.
|
+ |
+ void SetGeolocationProviderForTest(GeolocationProvider* provider); |
+ |
+ static const unsigned int kPollIntervalSeconds = 1800; |
+ |
+ private: |
+ void Init(const Geoposition& position); |
+ void DestructInternal(); |
+ |
+ void ScheduleUpdate(const Geoposition& position); |
+ |
+ void StartObserving(); |
+ void StopObserving(); |
+ |
+ void ReceiveUpdate(const Geoposition& position); |
+ |
+ // Used by UI thread only |
+ PrefService* local_state_; |
+ Geoposition position_; |
+ |
+ // Used by IO thread only |
+ base::OneShotTimer<DeviceStatusLocationHelper> *timer_; |
+ GeolocationProvider* provider_; |
+ bool observing_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(DeviceStatusLocationHelper); |
+}; |
+ |
+DeviceStatusLocationHelper::DeviceStatusLocationHelper( |
+ PrefService* local_state) |
+ : local_state_(local_state), |
+ timer_(NULL), |
+ provider_(NULL), |
+ observing_(false) { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
+ Geoposition position; |
+ std::string timestamp_str; |
+ int64 timestamp; |
+ const base::DictionaryValue* location = |
+ local_state_->GetDictionary(prefs::kDeviceLocation); |
+ if (location->GetDouble("latitude", &position.latitude) && |
+ location->GetDouble("longitude", &position.longitude) && |
+ location->GetDouble("altitude", &position.altitude) && |
+ location->GetDouble("accuracy", &position.accuracy) && |
+ location->GetDouble("altitude_accuracy", |
+ &position.altitude_accuracy) && |
+ location->GetDouble("heading", &position.heading) && |
+ location->GetDouble("speed", &position.speed) && |
+ location->GetString("timestamp", ×tamp_str) && |
+ base::StringToInt64(timestamp_str, ×tamp)) { |
+ position.timestamp = base::Time::FromInternalValue(timestamp); |
Joao da Silva
2012/04/20 09:49:57
Nit: base::Time -> Time
bartfab (slow)
2012/04/20 14:10:15
It moved to its own .cc file without "using base::
|
+ position_ = position; |
+ } |
+ content::BrowserThread::PostTask( |
+ content::BrowserThread::IO, |
+ FROM_HERE, |
+ base::Bind(&DeviceStatusLocationHelper::Init, |
+ base::Unretained(this), |
+ position_)); |
+} |
+ |
+DeviceStatusLocationHelper::~DeviceStatusLocationHelper() { |
+ // Do not call the destructor directly. Call Destruct() instead. |
+ // The destructor is only public so that the DeleteSoon task can call it. |
Joao da Silva
2012/04/20 09:49:57
Make the dtor private, and add
friend class base:
bartfab (slow)
2012/04/20 14:10:15
Done.
|
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
+ DCHECK(local_state_ == NULL); |
+ DCHECK(timer_ == NULL); |
+} |
+ |
+void DeviceStatusLocationHelper::Destruct() { |
+ // This class has a specific shutdown sequence that ensures all classes it |
+ // interfaces with are notified on the appropriate threads and any tasks |
+ // posted to it are processed before destrucion. |
Joao da Silva
2012/04/20 09:49:57
*destruction
bartfab (slow)
2012/04/20 14:10:15
Done.
|
+ // |
+ // The shutdown sequence is initiated by calling Destruct() on the UI thread: |
+ // |
+ // 1. Destruct() makes the UI thread ignore any further updates it receives, |
+ // then posts DestructInner() to the IO thread. |
+ // 2. DestructInner() reaches the IO thread after any updates queued up for it |
Joao da Silva
2012/04/20 09:49:57
In these 2 lines: DestructInner -> DestructInterna
bartfab (slow)
2012/04/20 14:10:15
Done.
|
+ // have been processed. This function destroys the poll timer and detaches |
+ // from the GeolocationProvider so that no further updates can be received |
+ // by the IO thread. Then, it posts DeleteSoon() back to the UI thread. |
+ // 3. DeleteSoon() reaches the UI thread after any updates queued for it have |
+ // been processed. At this point, there are no more tasks queued up for |
+ // either of the threads and it is safe for the object to be deleted. |
+ // 4. ~DeviceStatusLocationHelper() is run by DeleteSoon() and the object RIP. |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
+ local_state_ = NULL; |
+ content::BrowserThread::PostTask( |
+ content::BrowserThread::IO, |
+ FROM_HERE, |
+ base::Bind(&DeviceStatusLocationHelper::DestructInternal, |
+ base::Unretained(this))); |
+} |
+ |
+const Geoposition& DeviceStatusLocationHelper::GetPosition() const { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
+ return position_; |
+} |
+ |
+// static |
+void DeviceStatusLocationHelper::RegisterPrefs(PrefService* local_state) { |
+ local_state->RegisterDictionaryPref(prefs::kDeviceLocation, |
+ new DictionaryValue); |
+} |
+ |
+void DeviceStatusLocationHelper::OnLocationUpdate(const Geoposition& position) { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); |
+ if (!position.IsValidFix()) |
+ return; |
+ content::BrowserThread::PostTask( |
+ content::BrowserThread::UI, |
+ FROM_HERE, |
+ base::Bind(&DeviceStatusLocationHelper::ReceiveUpdate, |
+ base::Unretained(this), position)); |
+ StopObserving(); |
+ ScheduleUpdate(position); |
+} |
+ |
+void DeviceStatusLocationHelper::SetGeolocationProviderForTest( |
+ GeolocationProvider* provider) { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); |
+ DCHECK(!provider_); |
+ provider_ = provider; |
+} |
+ |
+void DeviceStatusLocationHelper::Init(const Geoposition& position) { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); |
+ if (!provider_) |
+ provider_ = GeolocationProvider::GetInstance(); |
+ timer_ = new base::OneShotTimer<DeviceStatusLocationHelper>(); |
+ provider_->OnPermissionGranted(); |
Joao da Silva
2012/04/20 09:49:57
Doesn't this need a GURL?
bartfab (slow)
2012/04/20 14:10:15
The GURL parameter was not actually being used for
Joao da Silva
2012/04/20 14:21:09
I see, thanks for the clarification.
|
+ ScheduleUpdate(position); |
+} |
+ |
+void DeviceStatusLocationHelper::DestructInternal() { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); |
+ delete timer_; |
+ timer_ = 0; |
+ StopObserving(); |
+ content::BrowserThread::DeleteSoon(content::BrowserThread::UI, |
+ FROM_HERE, |
+ this); |
+} |
+ |
+void DeviceStatusLocationHelper::ScheduleUpdate(const Geoposition& position) { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); |
+ if (position.IsValidFix()) { |
+ TimeDelta elapsed = Time::Now() - position.timestamp; |
+ TimeDelta interval = TimeDelta::FromSeconds(kPollIntervalSeconds); |
+ if (elapsed > interval) |
+ StartObserving(); |
+ timer_->Start(FROM_HERE, interval - elapsed, this, |
+ &DeviceStatusLocationHelper::StartObserving); |
Joao da Silva
2012/04/20 09:49:57
Shouldn't this be in an else block here, so that i
bartfab (slow)
2012/04/20 14:10:15
Done.
|
+ } else { |
+ StartObserving(); |
+ } |
+} |
+ |
+void DeviceStatusLocationHelper::StartObserving() { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); |
+ timer_->Stop(); |
+ provider_->AddObserver(this, GeolocationObserverOptions(true)); |
+ observing_ = true; |
+} |
+ |
+void DeviceStatusLocationHelper::StopObserving() { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); |
+ if (!observing_) |
+ return; |
+ provider_->RemoveObserver(this); |
+ observing_ = false; |
+} |
+ |
+void DeviceStatusLocationHelper::ReceiveUpdate(const Geoposition& position) { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
+ if (!local_state_) |
+ return; |
+ position_ = position; |
+ DictionaryValue location; |
+ location.SetDouble("latitude", position.latitude); |
+ location.SetDouble("longitude", position.longitude); |
+ location.SetDouble("altitude", position.altitude); |
+ location.SetDouble("accuracy", position.accuracy); |
+ location.SetDouble("altitude_accuracy", position.altitude_accuracy); |
+ location.SetDouble("heading", position.heading); |
+ location.SetDouble("speed", position.speed); |
+ location.SetString("timestamp", |
+ base::Int64ToString(position.timestamp.ToInternalValue())); |
+ local_state_->Set(prefs::kDeviceLocation, location); |
+} |
+ |
DeviceStatusCollector::DeviceStatusCollector( |
PrefService* local_state, |
chromeos::system::StatisticsProvider* provider) |
@@ -61,9 +267,12 @@ DeviceStatusCollector::DeviceStatusCollector( |
local_state_(local_state), |
last_idle_check_(Time()), |
statistics_provider_(provider), |
+ geolocation_provider_for_test_(NULL), |
+ location_helper_(NULL), |
report_version_info_(false), |
report_activity_times_(false), |
- report_boot_mode_(false) { |
+ report_boot_mode_(false), |
+ report_location_(false) { |
timer_.Start(FROM_HERE, |
TimeDelta::FromSeconds(kPollIntervalSeconds), |
this, &DeviceStatusCollector::CheckIdleState); |
@@ -76,6 +285,7 @@ DeviceStatusCollector::DeviceStatusCollector( |
cros_settings_->AddSettingsObserver(chromeos::kReportDeviceActivityTimes, |
this); |
cros_settings_->AddSettingsObserver(chromeos::kReportDeviceBootMode, this); |
+ cros_settings_->AddSettingsObserver(chromeos::kReportDeviceLocation, this); |
// Fetch the current values of the policies. |
UpdateReportingSettings(); |
@@ -96,12 +306,17 @@ DeviceStatusCollector::~DeviceStatusCollector() { |
cros_settings_->RemoveSettingsObserver(chromeos::kReportDeviceActivityTimes, |
this); |
cros_settings_->RemoveSettingsObserver(chromeos::kReportDeviceBootMode, this); |
+ cros_settings_->RemoveSettingsObserver(chromeos::kReportDeviceLocation, this); |
+ |
+ if (location_helper_) |
+ location_helper_->Destruct(); |
} |
// static |
void DeviceStatusCollector::RegisterPrefs(PrefService* local_state) { |
local_state->RegisterDictionaryPref(prefs::kDeviceActivityTimes, |
new DictionaryValue); |
+ DeviceStatusLocationHelper::RegisterPrefs(local_state); |
} |
void DeviceStatusCollector::CheckIdleState() { |
@@ -110,6 +325,13 @@ void DeviceStatusCollector::CheckIdleState() { |
base::Unretained(this))); |
} |
+void DeviceStatusCollector::SetGeolocationProviderForTest( |
+ GeolocationProvider* provider) { |
+ geolocation_provider_for_test_ = provider; |
+ if (location_helper_) |
+ location_helper_->SetGeolocationProviderForTest(provider); |
+} |
+ |
void DeviceStatusCollector::UpdateReportingSettings() { |
// Attempt to fetch the current value of the reporting settings. |
// If trusted values are not available, register this function to be called |
@@ -125,6 +347,23 @@ void DeviceStatusCollector::UpdateReportingSettings() { |
chromeos::kReportDeviceActivityTimes, &report_activity_times_); |
cros_settings_->GetBoolean( |
chromeos::kReportDeviceBootMode, &report_boot_mode_); |
+ cros_settings_->GetBoolean( |
+ chromeos::kReportDeviceLocation, &report_location_); |
+ |
+ if (report_location_) { |
+ if (!location_helper_) { |
+ location_helper_ = new DeviceStatusLocationHelper(local_state_); |
+ if (geolocation_provider_for_test_) |
Joao da Silva
2012/04/20 09:49:57
Braces around the block when it spans more than 1
bartfab (slow)
2012/04/20 14:10:15
Done.
|
+ location_helper_->SetGeolocationProviderForTest( |
+ geolocation_provider_for_test_); |
+ } |
+ } else { |
+ if (location_helper_) { |
+ location_helper_->Destruct(); |
+ location_helper_ = NULL; |
+ } |
+ local_state_->ClearPref(prefs::kDeviceLocation); |
+ } |
} |
Time DeviceStatusCollector::GetCurrentTime() { |
@@ -258,6 +497,37 @@ void DeviceStatusCollector::GetBootMode( |
} |
} |
+void DeviceStatusCollector::GetLocation( |
+ em::DeviceStatusReportRequest* request) { |
+ em::DeviceLocation* location = request->mutable_device_location(); |
+ if (!location_helper_) { |
+ location->set_error_code( |
+ em::DeviceLocation::ERROR_CODE_POSITION_UNAVAILABLE); |
+ } else { |
+ const Geoposition& position = location_helper_->GetPosition(); |
+ if (!position.IsValidFix()) { |
+ location->set_error_code( |
+ em::DeviceLocation::ERROR_CODE_POSITION_UNAVAILABLE); |
+ location->set_error_message(position.error_message); |
+ } else { |
+ location->set_latitude(position.latitude); |
+ location->set_longitude(position.longitude); |
+ location->set_accuracy(position.accuracy); |
+ location->set_timestamp( |
+ (position.timestamp - Time::UnixEpoch()).InMilliseconds()); |
+ if (position.is_valid_altitude()) |
+ location->set_altitude(position.altitude); |
+ if (position.is_valid_altitude_accuracy()) |
+ location->set_altitude_accuracy(position.altitude_accuracy); |
+ if (position.is_valid_heading()) |
+ location->set_heading(position.heading); |
+ if (position.is_valid_speed()) |
+ location->set_speed(position.speed); |
+ location->set_error_code(em::DeviceLocation::ERROR_CODE_NONE); |
+ } |
+ } |
+} |
+ |
void DeviceStatusCollector::GetStatus(em::DeviceStatusReportRequest* request) { |
if (report_activity_times_) |
GetActivityTimes(request); |
@@ -267,6 +537,9 @@ void DeviceStatusCollector::GetStatus(em::DeviceStatusReportRequest* request) { |
if (report_boot_mode_) |
GetBootMode(request); |
+ |
+ if (report_location_) |
+ GetLocation(request); |
} |
void DeviceStatusCollector::OnOSVersion(VersionLoader::Handle handle, |