device_status_collector.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
1// Copyright (c) 2012 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/chromeos/policy/device_status_collector.h" 6 7#include <limits> 8 9#include "base/bind.h" 10#include "base/bind_helpers.h" 11#include "base/location.h" 12#include "base/logging.h" 13#include "base/memory/scoped_ptr.h" 14#include "base/prefs/pref_registry_simple.h" 15#include "base/prefs/pref_service.h" 16#include "base/strings/string_number_conversions.h" 17#include "base/values.h" 18#include "chrome/browser/chromeos/settings/cros_settings.h" 19#include "chrome/browser/chromeos/settings/cros_settings_names.h" 20#include "chrome/browser/chromeos/system/statistics_provider.h" 21#include "chrome/browser/policy/proto/cloud/device_management_backend.pb.h" 22#include "chrome/browser/prefs/scoped_user_pref_update.h" 23#include "chrome/common/chrome_notification_types.h" 24#include "chrome/common/chrome_version_info.h" 25#include "chrome/common/pref_names.h" 26#include "content/public/browser/notification_details.h" 27#include "content/public/browser/notification_source.h" 28 29using base::Time; 30using base::TimeDelta; 31using chromeos::VersionLoader; 32 33namespace em = enterprise_management; 34 35namespace { 36// How many seconds of inactivity triggers the idle state. 37const int kIdleStateThresholdSeconds = 300; 38 39// How many days in the past to store active periods for. 40const unsigned int kMaxStoredPastActivityDays = 30; 41 42// How many days in the future to store active periods for. 43const unsigned int kMaxStoredFutureActivityDays = 2; 44 45// How often, in seconds, to update the device location. 46const unsigned int kGeolocationPollIntervalSeconds = 30 * 60; 47 48const int64 kMillisecondsPerDay = Time::kMicrosecondsPerDay / 1000; 49 50const char kLatitude[] = "latitude"; 51 52const char kLongitude[] = "longitude"; 53 54const char kAltitude[] = "altitude"; 55 56const char kAccuracy[] = "accuracy"; 57 58const char kAltitudeAccuracy[] = "altitude_accuracy"; 59 60const char kHeading[] = "heading"; 61 62const char kSpeed[] = "speed"; 63 64const char kTimestamp[] = "timestamp"; 65 66// Determine the day key (milliseconds since epoch for corresponding day in UTC) 67// for a given |timestamp|. 68int64 TimestampToDayKey(Time timestamp) { 69 Time::Exploded exploded; 70 timestamp.LocalMidnight().LocalExplode(&exploded); 71 return (Time::FromUTCExploded(exploded) - Time::UnixEpoch()).InMilliseconds(); 72} 73 74} // namespace 75 76namespace policy { 77 78DeviceStatusCollector::DeviceStatusCollector( 79 PrefService* local_state, 80 chromeos::system::StatisticsProvider* provider, 81 LocationUpdateRequester location_update_requester) 82 : max_stored_past_activity_days_(kMaxStoredPastActivityDays), 83 max_stored_future_activity_days_(kMaxStoredFutureActivityDays), 84 local_state_(local_state), 85 last_idle_check_(Time()), 86 last_reported_day_(0), 87 duration_for_last_reported_day_(0), 88 geolocation_update_in_progress_(false), 89 statistics_provider_(provider), 90 weak_factory_(this), 91 location_update_requester_(location_update_requester), 92 report_version_info_(false), 93 report_activity_times_(false), 94 report_boot_mode_(false), 95 report_location_(false) { 96 if (!location_update_requester_) 97 location_update_requester_ = &content::RequestLocationUpdate; 98 idle_poll_timer_.Start(FROM_HERE, 99 TimeDelta::FromSeconds(kIdlePollIntervalSeconds), 100 this, &DeviceStatusCollector::CheckIdleState); 101 102 cros_settings_ = chromeos::CrosSettings::Get(); 103 104 // Watch for changes to the individual policies that control what the status 105 // reports contain. 106 cros_settings_->AddSettingsObserver(chromeos::kReportDeviceVersionInfo, this); 107 cros_settings_->AddSettingsObserver(chromeos::kReportDeviceActivityTimes, 108 this); 109 cros_settings_->AddSettingsObserver(chromeos::kReportDeviceBootMode, this); 110 cros_settings_->AddSettingsObserver(chromeos::kReportDeviceLocation, this); 111 112 // The last known location is persisted in local state. This makes location 113 // information available immediately upon startup and avoids the need to 114 // reacquire the location on every user session change or browser crash. 115 content::Geoposition position; 116 std::string timestamp_str; 117 int64 timestamp; 118 const base::DictionaryValue* location = 119 local_state_->GetDictionary(prefs::kDeviceLocation); 120 if (location->GetDouble(kLatitude, &position.latitude) && 121 location->GetDouble(kLongitude, &position.longitude) && 122 location->GetDouble(kAltitude, &position.altitude) && 123 location->GetDouble(kAccuracy, &position.accuracy) && 124 location->GetDouble(kAltitudeAccuracy, &position.altitude_accuracy) && 125 location->GetDouble(kHeading, &position.heading) && 126 location->GetDouble(kSpeed, &position.speed) && 127 location->GetString(kTimestamp, ×tamp_str) && 128 base::StringToInt64(timestamp_str, ×tamp)) { 129 position.timestamp = Time::FromInternalValue(timestamp); 130 position_ = position; 131 } 132 133 // Fetch the current values of the policies. 134 UpdateReportingSettings(); 135 136 // Get the the OS and firmware version info. 137 version_loader_.GetVersion( 138 VersionLoader::VERSION_FULL, 139 base::Bind(&DeviceStatusCollector::OnOSVersion, base::Unretained(this)), 140 &tracker_); 141 version_loader_.GetFirmware( 142 base::Bind(&DeviceStatusCollector::OnOSFirmware, base::Unretained(this)), 143 &tracker_); 144} 145 146DeviceStatusCollector::~DeviceStatusCollector() { 147 cros_settings_->RemoveSettingsObserver(chromeos::kReportDeviceVersionInfo, 148 this); 149 cros_settings_->RemoveSettingsObserver(chromeos::kReportDeviceActivityTimes, 150 this); 151 cros_settings_->RemoveSettingsObserver(chromeos::kReportDeviceBootMode, this); 152 cros_settings_->RemoveSettingsObserver(chromeos::kReportDeviceLocation, this); 153} 154 155// static 156void DeviceStatusCollector::RegisterPrefs(PrefRegistrySimple* registry) { 157 registry->RegisterDictionaryPref(prefs::kDeviceActivityTimes, 158 new base::DictionaryValue); 159 registry->RegisterDictionaryPref(prefs::kDeviceLocation, 160 new base::DictionaryValue); 161} 162 163void DeviceStatusCollector::CheckIdleState() { 164 CalculateIdleState(kIdleStateThresholdSeconds, 165 base::Bind(&DeviceStatusCollector::IdleStateCallback, 166 base::Unretained(this))); 167} 168 169void DeviceStatusCollector::UpdateReportingSettings() { 170 // Attempt to fetch the current value of the reporting settings. 171 // If trusted values are not available, register this function to be called 172 // back when they are available. 173 if (chromeos::CrosSettingsProvider::TRUSTED != 174 cros_settings_->PrepareTrustedValues( 175 base::Bind(&DeviceStatusCollector::UpdateReportingSettings, 176 weak_factory_.GetWeakPtr()))) { 177 return; 178 } 179 cros_settings_->GetBoolean( 180 chromeos::kReportDeviceVersionInfo, &report_version_info_); 181 cros_settings_->GetBoolean( 182 chromeos::kReportDeviceActivityTimes, &report_activity_times_); 183 cros_settings_->GetBoolean( 184 chromeos::kReportDeviceBootMode, &report_boot_mode_); 185 cros_settings_->GetBoolean( 186 chromeos::kReportDeviceLocation, &report_location_); 187 188 if (report_location_) { 189 ScheduleGeolocationUpdateRequest(); 190 } else { 191 geolocation_update_timer_.Stop(); 192 position_ = content::Geoposition(); 193 local_state_->ClearPref(prefs::kDeviceLocation); 194 } 195} 196 197Time DeviceStatusCollector::GetCurrentTime() { 198 return Time::Now(); 199} 200 201// Remove all out-of-range activity times from the local store. 202void DeviceStatusCollector::PruneStoredActivityPeriods(Time base_time) { 203 Time min_time = 204 base_time - TimeDelta::FromDays(max_stored_past_activity_days_); 205 Time max_time = 206 base_time + TimeDelta::FromDays(max_stored_future_activity_days_); 207 TrimStoredActivityPeriods(TimestampToDayKey(min_time), 0, 208 TimestampToDayKey(max_time)); 209} 210 211void DeviceStatusCollector::TrimStoredActivityPeriods(int64 min_day_key, 212 int min_day_trim_duration, 213 int64 max_day_key) { 214 const base::DictionaryValue* activity_times = 215 local_state_->GetDictionary(prefs::kDeviceActivityTimes); 216 217 scoped_ptr<base::DictionaryValue> copy(activity_times->DeepCopy()); 218 for (base::DictionaryValue::Iterator it(*activity_times); !it.IsAtEnd(); 219 it.Advance()) { 220 int64 timestamp; 221 if (base::StringToInt64(it.key(), ×tamp)) { 222 // Remove data that is too old, or too far in the future. 223 if (timestamp >= min_day_key && timestamp < max_day_key) { 224 if (timestamp == min_day_key) { 225 int new_activity_duration = 0; 226 if (it.value().GetAsInteger(&new_activity_duration)) { 227 new_activity_duration = 228 std::max(new_activity_duration - min_day_trim_duration, 0); 229 } 230 copy->SetInteger(it.key(), new_activity_duration); 231 } 232 continue; 233 } 234 } 235 // The entry is out of range or couldn't be parsed. Remove it. 236 copy->Remove(it.key(), NULL); 237 } 238 local_state_->Set(prefs::kDeviceActivityTimes, *copy); 239} 240 241void DeviceStatusCollector::AddActivePeriod(Time start, Time end) { 242 DCHECK(start < end); 243 244 // Maintain the list of active periods in a local_state pref. 245 DictionaryPrefUpdate update(local_state_, prefs::kDeviceActivityTimes); 246 base::DictionaryValue* activity_times = update.Get(); 247 248 // Assign the period to day buckets in local time. 249 Time midnight = start.LocalMidnight(); 250 while (midnight < end) { 251 midnight += TimeDelta::FromDays(1); 252 int64 activity = (std::min(end, midnight) - start).InMilliseconds(); 253 std::string day_key = base::Int64ToString(TimestampToDayKey(start)); 254 int previous_activity = 0; 255 activity_times->GetInteger(day_key, &previous_activity); 256 activity_times->SetInteger(day_key, previous_activity + activity); 257 start = midnight; 258 } 259} 260 261void DeviceStatusCollector::IdleStateCallback(IdleState state) { 262 // Do nothing if device activity reporting is disabled. 263 if (!report_activity_times_) 264 return; 265 266 Time now = GetCurrentTime(); 267 268 if (state == IDLE_STATE_ACTIVE) { 269 // If it's been too long since the last report, or if the activity is 270 // negative (which can happen when the clock changes), assume a single 271 // interval of activity. 272 int active_seconds = (now - last_idle_check_).InSeconds(); 273 if (active_seconds < 0 || 274 active_seconds >= static_cast<int>((2 * kIdlePollIntervalSeconds))) { 275 AddActivePeriod(now - TimeDelta::FromSeconds(kIdlePollIntervalSeconds), 276 now); 277 } else { 278 AddActivePeriod(last_idle_check_, now); 279 } 280 281 PruneStoredActivityPeriods(now); 282 } 283 last_idle_check_ = now; 284} 285 286void DeviceStatusCollector::GetActivityTimes( 287 em::DeviceStatusReportRequest* request) { 288 DictionaryPrefUpdate update(local_state_, prefs::kDeviceActivityTimes); 289 base::DictionaryValue* activity_times = update.Get(); 290 291 for (base::DictionaryValue::Iterator it(*activity_times); !it.IsAtEnd(); 292 it.Advance()) { 293 int64 start_timestamp; 294 int activity_milliseconds; 295 if (base::StringToInt64(it.key(), &start_timestamp) && 296 it.value().GetAsInteger(&activity_milliseconds)) { 297 // This is correct even when there are leap seconds, because when a leap 298 // second occurs, two consecutive seconds have the same timestamp. 299 int64 end_timestamp = start_timestamp + kMillisecondsPerDay; 300 301 em::ActiveTimePeriod* active_period = request->add_active_period(); 302 em::TimePeriod* period = active_period->mutable_time_period(); 303 period->set_start_timestamp(start_timestamp); 304 period->set_end_timestamp(end_timestamp); 305 active_period->set_active_duration(activity_milliseconds); 306 if (start_timestamp >= last_reported_day_) { 307 last_reported_day_ = start_timestamp; 308 duration_for_last_reported_day_ = activity_milliseconds; 309 } 310 } else { 311 NOTREACHED(); 312 } 313 } 314} 315 316void DeviceStatusCollector::GetVersionInfo( 317 em::DeviceStatusReportRequest* request) { 318 chrome::VersionInfo version_info; 319 request->set_browser_version(version_info.Version()); 320 request->set_os_version(os_version_); 321 request->set_firmware_version(firmware_version_); 322} 323 324void DeviceStatusCollector::GetBootMode( 325 em::DeviceStatusReportRequest* request) { 326 std::string dev_switch_mode; 327 if (statistics_provider_->GetMachineStatistic( 328 "devsw_boot", &dev_switch_mode)) { 329 if (dev_switch_mode == "1") 330 request->set_boot_mode("Dev"); 331 else if (dev_switch_mode == "0") 332 request->set_boot_mode("Verified"); 333 } 334} 335 336void DeviceStatusCollector::GetLocation( 337 em::DeviceStatusReportRequest* request) { 338 em::DeviceLocation* location = request->mutable_device_location(); 339 if (!position_.Validate()) { 340 location->set_error_code( 341 em::DeviceLocation::ERROR_CODE_POSITION_UNAVAILABLE); 342 location->set_error_message(position_.error_message); 343 } else { 344 location->set_latitude(position_.latitude); 345 location->set_longitude(position_.longitude); 346 location->set_accuracy(position_.accuracy); 347 location->set_timestamp( 348 (position_.timestamp - Time::UnixEpoch()).InMilliseconds()); 349 // Lowest point on land is at approximately -400 meters. 350 if (position_.altitude > -10000.) 351 location->set_altitude(position_.altitude); 352 if (position_.altitude_accuracy >= 0.) 353 location->set_altitude_accuracy(position_.altitude_accuracy); 354 if (position_.heading >= 0. && position_.heading <= 360) 355 location->set_heading(position_.heading); 356 if (position_.speed >= 0.) 357 location->set_speed(position_.speed); 358 location->set_error_code(em::DeviceLocation::ERROR_CODE_NONE); 359 } 360} 361 362void DeviceStatusCollector::GetStatus(em::DeviceStatusReportRequest* request) { 363 // TODO(mnissler): Remove once the old cloud policy stack is retired. The old 364 // stack doesn't support reporting successful submissions back to here, so 365 // just assume whatever ends up in |request| gets submitted successfully. 366 GetDeviceStatus(request); 367 OnSubmittedSuccessfully(); 368} 369 370bool DeviceStatusCollector::GetDeviceStatus( 371 em::DeviceStatusReportRequest* status) { 372 if (report_activity_times_) 373 GetActivityTimes(status); 374 375 if (report_version_info_) 376 GetVersionInfo(status); 377 378 if (report_boot_mode_) 379 GetBootMode(status); 380 381 if (report_location_) 382 GetLocation(status); 383 384 return true; 385} 386 387bool DeviceStatusCollector::GetSessionStatus( 388 em::SessionStatusReportRequest* status) { 389 return false; 390} 391 392void DeviceStatusCollector::OnSubmittedSuccessfully() { 393 TrimStoredActivityPeriods(last_reported_day_, duration_for_last_reported_day_, 394 std::numeric_limits<int64>::max()); 395} 396 397void DeviceStatusCollector::OnOSVersion(const std::string& version) { 398 os_version_ = version; 399} 400 401void DeviceStatusCollector::OnOSFirmware(const std::string& version) { 402 firmware_version_ = version; 403} 404 405void DeviceStatusCollector::Observe( 406 int type, 407 const content::NotificationSource& source, 408 const content::NotificationDetails& details) { 409 if (type == chrome::NOTIFICATION_SYSTEM_SETTING_CHANGED) 410 UpdateReportingSettings(); 411 else 412 NOTREACHED(); 413} 414 415void DeviceStatusCollector::ScheduleGeolocationUpdateRequest() { 416 if (geolocation_update_timer_.IsRunning() || geolocation_update_in_progress_) 417 return; 418 419 if (position_.Validate()) { 420 TimeDelta elapsed = GetCurrentTime() - position_.timestamp; 421 TimeDelta interval = 422 TimeDelta::FromSeconds(kGeolocationPollIntervalSeconds); 423 if (elapsed > interval) { 424 geolocation_update_in_progress_ = true; 425 location_update_requester_(base::Bind( 426 &DeviceStatusCollector::ReceiveGeolocationUpdate, 427 weak_factory_.GetWeakPtr())); 428 } else { 429 geolocation_update_timer_.Start( 430 FROM_HERE, 431 interval - elapsed, 432 this, 433 &DeviceStatusCollector::ScheduleGeolocationUpdateRequest); 434 } 435 } else { 436 geolocation_update_in_progress_ = true; 437 location_update_requester_(base::Bind( 438 &DeviceStatusCollector::ReceiveGeolocationUpdate, 439 weak_factory_.GetWeakPtr())); 440 441 } 442} 443 444void DeviceStatusCollector::ReceiveGeolocationUpdate( 445 const content::Geoposition& position) { 446 geolocation_update_in_progress_ = false; 447 448 // Ignore update if device location reporting has since been disabled. 449 if (!report_location_) 450 return; 451 452 if (position.Validate()) { 453 position_ = position; 454 base::DictionaryValue location; 455 location.SetDouble(kLatitude, position.latitude); 456 location.SetDouble(kLongitude, position.longitude); 457 location.SetDouble(kAltitude, position.altitude); 458 location.SetDouble(kAccuracy, position.accuracy); 459 location.SetDouble(kAltitudeAccuracy, position.altitude_accuracy); 460 location.SetDouble(kHeading, position.heading); 461 location.SetDouble(kSpeed, position.speed); 462 location.SetString(kTimestamp, 463 base::Int64ToString(position.timestamp.ToInternalValue())); 464 local_state_->Set(prefs::kDeviceLocation, location); 465 } 466 467 ScheduleGeolocationUpdateRequest(); 468} 469 470} // namespace policy 471