Chromium Code Reviews| 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, |