diff options
author | dubroy@chromium.org <dubroy@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-12-09 11:40:49 +0000 |
---|---|---|
committer | dubroy@chromium.org <dubroy@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-12-09 11:40:49 +0000 |
commit | 9e5fbc9ea76ce84daa984f3dc0dde7c3478a7fc1 (patch) | |
tree | 2622c03c01a302d60fa82b376618c8fb2aed5869 /chrome | |
parent | 59fe8f8f4807dd692bd9a9b99e08f400553f4d2c (diff) | |
download | chromium_src-9e5fbc9ea76ce84daa984f3dc0dde7c3478a7fc1.zip chromium_src-9e5fbc9ea76ce84daa984f3dc0dde7c3478a7fc1.tar.gz chromium_src-9e5fbc9ea76ce84daa984f3dc0dde7c3478a7fc1.tar.bz2 |
Add device status reports to policy requests.
BUG=chromium-os:22035
TEST=
Review URL: http://codereview.chromium.org/8702009
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@113791 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
22 files changed, 523 insertions, 31 deletions
diff --git a/chrome/browser/idle.h b/chrome/browser/idle.h index dfda8d7..4c9e791 100644 --- a/chrome/browser/idle.h +++ b/chrome/browser/idle.h @@ -10,7 +10,7 @@ enum IdleState { IDLE_STATE_ACTIVE = 0, - IDLE_STATE_IDLE = 1, // No activity within threshold. + IDLE_STATE_IDLE = 1, // No activity within threshold. IDLE_STATE_LOCKED = 2, // Only available on supported systems. IDLE_STATE_UNKNOWN = 3 // Used when waiting for the Idle state or in error // conditions diff --git a/chrome/browser/policy/browser_policy_connector.cc b/chrome/browser/policy/browser_policy_connector.cc index 03af03a..17decb0 100644 --- a/chrome/browser/policy/browser_policy_connector.cc +++ b/chrome/browser/policy/browser_policy_connector.cc @@ -8,6 +8,7 @@ #include "base/command_line.h" #include "base/file_path.h" #include "base/path_service.h" +#include "chrome/browser/browser_process.h" #include "chrome/browser/net/gaia/token_service.h" #include "chrome/browser/policy/cloud_policy_provider.h" #include "chrome/browser/policy/cloud_policy_provider_impl.h" @@ -422,19 +423,21 @@ void BrowserPolicyConnector::InitializeDevicePolicy() { // Initialize the subsystem once the message loops are spinning. MessageLoop::current()->PostTask( FROM_HERE, - base::Bind(&BrowserPolicyConnector::InitializeDevicePolicySubsystem, + base::Bind(&BrowserPolicyConnector::CompleteInitialization, weak_ptr_factory_.GetWeakPtr())); } #endif } -void BrowserPolicyConnector::InitializeDevicePolicySubsystem() { +void BrowserPolicyConnector::CompleteInitialization() { #if defined(OS_CHROMEOS) if (device_cloud_policy_subsystem_.get()) { device_cloud_policy_subsystem_->CompleteInitialization( prefs::kDevicePolicyRefreshRate, kServiceInitializationStartupDelay); } + device_data_store_->set_device_status_collector( + new DeviceStatusCollector(g_browser_process->local_state())); #endif } diff --git a/chrome/browser/policy/browser_policy_connector.h b/chrome/browser/policy/browser_policy_connector.h index 50d4462..4f2fcf6 100644 --- a/chrome/browser/policy/browser_policy_connector.h +++ b/chrome/browser/policy/browser_policy_connector.h @@ -139,10 +139,9 @@ class BrowserPolicyConnector : public content::NotificationObserver { // Initializes the device cloud policy infrasturcture. void InitializeDevicePolicy(); - // Activates the device cloud policy subsystem. This will be posted as a task - // from InitializeDevicePolicy since it needs to wait for the message loops to - // be running. - void InitializeDevicePolicySubsystem(); + // Complete initialization once the message loops are running and the + // local_state is initialized. + void CompleteInitialization(); static ConfigurationPolicyProvider* CreateManagedPlatformProvider(); static ConfigurationPolicyProvider* CreateRecommendedPlatformProvider(); diff --git a/chrome/browser/policy/cloud_policy_controller.cc b/chrome/browser/policy/cloud_policy_controller.cc index 93f450e..9858e28 100644 --- a/chrome/browser/policy/cloud_policy_controller.cc +++ b/chrome/browser/policy/cloud_policy_controller.cc @@ -262,6 +262,7 @@ void CloudPolicyController::SendPolicyRequest() { DCHECK(!data_store_->device_token().empty()); em::DevicePolicyRequest policy_request; em::PolicyFetchRequest* fetch_request = policy_request.add_request(); + em::DeviceStatusReportRequest device_status; fetch_request->set_signature_type(em::PolicyFetchRequest::SHA1_RSA); fetch_request->set_policy_type(data_store_->policy_type()); if (!cache_->is_unmanaged() && @@ -274,10 +275,13 @@ void CloudPolicyController::SendPolicyRequest() { if (cache_->GetPublicKeyVersion(&key_version)) fetch_request->set_public_key_version(key_version); + if (data_store_->device_status_collector()) + data_store_->device_status_collector()->GetStatus(&device_status); + backend_->ProcessPolicyRequest(data_store_->device_token(), data_store_->device_id(), data_store_->user_affiliation(), - policy_request, this); + policy_request, &device_status, this); } void CloudPolicyController::DoWork() { diff --git a/chrome/browser/policy/cloud_policy_controller_unittest.cc b/chrome/browser/policy/cloud_policy_controller_unittest.cc index 8636c5c..bf31643 100644 --- a/chrome/browser/policy/cloud_policy_controller_unittest.cc +++ b/chrome/browser/policy/cloud_policy_controller_unittest.cc @@ -117,7 +117,7 @@ class CloudPolicyControllerTest : public testing::Test { TEST_F(CloudPolicyControllerTest, StartupWithDeviceToken) { data_store_->SetupForTesting("fake_device_token", "device_id", "", "", true); - EXPECT_CALL(backend_, ProcessPolicyRequest(_, _, _, _, _)).WillOnce(DoAll( + EXPECT_CALL(backend_, ProcessPolicyRequest(_, _, _, _, _, _)).WillOnce(DoAll( InvokeWithoutArgs(this, &CloudPolicyControllerTest::StopMessageLoop), MockDeviceManagementBackendSucceedSpdyCloudPolicy())); CreateNewController(); @@ -153,9 +153,10 @@ TEST_F(CloudPolicyControllerTest, RefreshAfterSuccessfulPolicy) { "auth_token", true); { InSequence s; - EXPECT_CALL(backend_, ProcessPolicyRequest(_, _, _, _, _)).WillOnce( + EXPECT_CALL(backend_, ProcessPolicyRequest(_, _, _, _, _, _)).WillOnce( MockDeviceManagementBackendSucceedSpdyCloudPolicy()); - EXPECT_CALL(backend_, ProcessPolicyRequest(_, _, _, _, _)).WillOnce(DoAll( + EXPECT_CALL(backend_, + ProcessPolicyRequest(_, _, _, _, _, _)).WillOnce(DoAll( InvokeWithoutArgs(this, &CloudPolicyControllerTest::StopMessageLoop), MockDeviceManagementBackendFailPolicy( DeviceManagementBackend::kErrorRequestFailed))); @@ -172,10 +173,11 @@ TEST_F(CloudPolicyControllerTest, RefreshAfterError) { "auth_token", true); { InSequence s; - EXPECT_CALL(backend_, ProcessPolicyRequest(_, _, _, _, _)).WillOnce( + EXPECT_CALL(backend_, ProcessPolicyRequest(_, _, _, _, _, _)).WillOnce( MockDeviceManagementBackendFailPolicy( DeviceManagementBackend::kErrorRequestFailed)); - EXPECT_CALL(backend_, ProcessPolicyRequest(_, _, _, _, _)).WillOnce(DoAll( + EXPECT_CALL(backend_, + ProcessPolicyRequest(_, _, _, _, _, _)).WillOnce(DoAll( InvokeWithoutArgs(this, &CloudPolicyControllerTest::StopMessageLoop), MockDeviceManagementBackendSucceedSpdyCloudPolicy())); @@ -190,7 +192,7 @@ TEST_F(CloudPolicyControllerTest, RefreshAfterError) { TEST_F(CloudPolicyControllerTest, InvalidToken) { data_store_->SetupForTesting("device_token", "device_id", "standup@ten.am", "auth", true); - EXPECT_CALL(backend_, ProcessPolicyRequest(_, _, _, _, _)).WillOnce( + EXPECT_CALL(backend_, ProcessPolicyRequest(_, _, _, _, _, _)).WillOnce( MockDeviceManagementBackendFailPolicy( DeviceManagementBackend::kErrorServiceManagementTokenInvalid)); EXPECT_CALL(*token_fetcher_.get(), FetchToken()).Times(1); @@ -203,7 +205,7 @@ TEST_F(CloudPolicyControllerTest, InvalidToken) { TEST_F(CloudPolicyControllerTest, DeviceNotFound) { data_store_->SetupForTesting("device_token", "device_id", "me@you.com", "auth", true); - EXPECT_CALL(backend_, ProcessPolicyRequest(_, _, _, _, _)).WillOnce( + EXPECT_CALL(backend_, ProcessPolicyRequest(_, _, _, _, _, _)).WillOnce( MockDeviceManagementBackendFailPolicy( DeviceManagementBackend::kErrorServiceDeviceNotFound)); EXPECT_CALL(*token_fetcher_.get(), FetchToken()).Times(1); @@ -216,7 +218,7 @@ TEST_F(CloudPolicyControllerTest, DeviceNotFound) { TEST_F(CloudPolicyControllerTest, DeviceIdConflict) { data_store_->SetupForTesting("device_token", "device_id", "me@you.com", "auth", true); - EXPECT_CALL(backend_, ProcessPolicyRequest(_, _, _, _, _)).WillOnce( + EXPECT_CALL(backend_, ProcessPolicyRequest(_, _, _, _, _, _)).WillOnce( MockDeviceManagementBackendFailPolicy( DeviceManagementBackend::kErrorServiceDeviceIdConflict)); EXPECT_CALL(*token_fetcher_.get(), FetchToken()).Times(1); @@ -230,7 +232,7 @@ TEST_F(CloudPolicyControllerTest, DeviceIdConflict) { TEST_F(CloudPolicyControllerTest, NoLongerManaged) { data_store_->SetupForTesting("device_token", "device_id", "who@what.com", "auth", true); - EXPECT_CALL(backend_, ProcessPolicyRequest(_, _, _, _, _)).WillOnce( + EXPECT_CALL(backend_, ProcessPolicyRequest(_, _, _, _, _, _)).WillOnce( MockDeviceManagementBackendFailPolicy( DeviceManagementBackend::kErrorServiceManagementNotSupported)); EXPECT_CALL(*token_fetcher_.get(), SetUnmanagedState()).Times(1); @@ -244,7 +246,7 @@ TEST_F(CloudPolicyControllerTest, NoLongerManaged) { TEST_F(CloudPolicyControllerTest, InvalidSerialNumber) { data_store_->SetupForTesting("device_token", "device_id", "who@what.com", "auth", true); - EXPECT_CALL(backend_, ProcessPolicyRequest(_, _, _, _, _)).WillOnce( + EXPECT_CALL(backend_, ProcessPolicyRequest(_, _, _, _, _, _)).WillOnce( MockDeviceManagementBackendFailPolicy( DeviceManagementBackend::kErrorServiceInvalidSerialNumber)); EXPECT_CALL(*token_fetcher_.get(), SetSerialNumberInvalidState()).Times(1); @@ -286,7 +288,7 @@ TEST_F(CloudPolicyControllerTest, SetFetchingDoneAfterPolicyFetch) { CreateNewWaitingCache(); data_store_->SetupForTesting("device_token", "device_id", "user@enterprise.com", "auth", true); - EXPECT_CALL(backend_, ProcessPolicyRequest(_, _, _, _, _)).WillOnce(DoAll( + EXPECT_CALL(backend_, ProcessPolicyRequest(_, _, _, _, _, _)).WillOnce(DoAll( InvokeWithoutArgs(this, &CloudPolicyControllerTest::StopMessageLoop), MockDeviceManagementBackendSucceedSpdyCloudPolicy())); CreateNewController(); @@ -299,7 +301,7 @@ TEST_F(CloudPolicyControllerTest, SetFetchingDoneAfterPolicyFetchFails) { CreateNewWaitingCache(); data_store_->SetupForTesting("device_token", "device_id", "user@enterprise.com", "auth", true); - EXPECT_CALL(backend_, ProcessPolicyRequest(_, _, _, _, _)).WillOnce(DoAll( + EXPECT_CALL(backend_, ProcessPolicyRequest(_, _, _, _, _, _)).WillOnce(DoAll( InvokeWithoutArgs(this, &CloudPolicyControllerTest::StopMessageLoop), MockDeviceManagementBackendFailPolicy( DeviceManagementBackend::kErrorRequestFailed))); diff --git a/chrome/browser/policy/cloud_policy_data_store.cc b/chrome/browser/policy/cloud_policy_data_store.cc index 1756823..7a72e9d 100644 --- a/chrome/browser/policy/cloud_policy_data_store.cc +++ b/chrome/browser/policy/cloud_policy_data_store.cc @@ -151,6 +151,16 @@ CloudPolicyDataStore::UserAffiliation return user_affiliation_; } +DeviceStatusCollector* + CloudPolicyDataStore::device_status_collector() { + return device_status_collector_.get(); +} + +void CloudPolicyDataStore::set_device_status_collector( + DeviceStatusCollector* collector) { + device_status_collector_.reset(collector); +} + void CloudPolicyDataStore::AddObserver( CloudPolicyDataStore::Observer* observer) { observer_list_.AddObserver(observer); diff --git a/chrome/browser/policy/cloud_policy_data_store.h b/chrome/browser/policy/cloud_policy_data_store.h index 626bd70..74e60a6 100644 --- a/chrome/browser/policy/cloud_policy_data_store.h +++ b/chrome/browser/policy/cloud_policy_data_store.h @@ -8,7 +8,9 @@ #include <string> +#include "base/memory/scoped_ptr.h" #include "base/observer_list.h" +#include "chrome/browser/policy/device_status_collector.h" #include "chrome/browser/policy/proto/device_management_backend.pb.h" namespace policy { @@ -80,6 +82,7 @@ class CloudPolicyDataStore { void set_machine_model(const std::string& machine_model); void set_user_name(const std::string& user_name); void set_user_affiliation(UserAffiliation user_affiliation); + void set_device_status_collector(DeviceStatusCollector* collector); const std::string& device_id() const; const std::string& device_token() const; @@ -93,6 +96,7 @@ class CloudPolicyDataStore { bool token_cache_loaded() const; const std::string& user_name() const; UserAffiliation user_affiliation() const; + DeviceStatusCollector* device_status_collector(); void AddObserver(Observer* observer); void RemoveObserver(Observer* observer); @@ -126,6 +130,8 @@ class CloudPolicyDataStore { bool token_cache_loaded_; + scoped_ptr<DeviceStatusCollector> device_status_collector_; + ObserverList<Observer, true> observer_list_; DISALLOW_COPY_AND_ASSIGN(CloudPolicyDataStore); diff --git a/chrome/browser/policy/device_management_backend.h b/chrome/browser/policy/device_management_backend.h index a832373..81c93c5 100644 --- a/chrome/browser/policy/device_management_backend.h +++ b/chrome/browser/policy/device_management_backend.h @@ -134,6 +134,7 @@ class DeviceManagementBackend : base::NonThreadSafe { const std::string& device_id, CloudPolicyDataStore::UserAffiliation user_affiliation, const em::DevicePolicyRequest& request, + const em::DeviceStatusReportRequest* device_status, DevicePolicyResponseDelegate* delegate) = 0; virtual void ProcessAutoEnrollmentRequest( diff --git a/chrome/browser/policy/device_management_backend_impl.cc b/chrome/browser/policy/device_management_backend_impl.cc index 42b92cb..3705654 100644 --- a/chrome/browser/policy/device_management_backend_impl.cc +++ b/chrome/browser/policy/device_management_backend_impl.cc @@ -407,6 +407,7 @@ class DeviceManagementPolicyJob : public DeviceManagementJobBase { const std::string& device_id, const std::string& user_affiliation, const em::DevicePolicyRequest& request, + const em::DeviceStatusReportRequest* device_status, DeviceManagementBackend::DevicePolicyResponseDelegate* delegate) : DeviceManagementJobBase( backend_impl, @@ -418,6 +419,10 @@ class DeviceManagementPolicyJob : public DeviceManagementJobBase { user_affiliation); em::DeviceManagementRequest request_wrapper; request_wrapper.mutable_policy_request()->CopyFrom(request); + if (device_status != NULL) { + request_wrapper.mutable_device_status_report_request()->CopyFrom( + *device_status); + } SetPayload(request_wrapper); } virtual ~DeviceManagementPolicyJob() {} @@ -602,12 +607,13 @@ void DeviceManagementBackendImpl::ProcessPolicyRequest( const std::string& device_id, CloudPolicyDataStore::UserAffiliation affiliation, const em::DevicePolicyRequest& request, + const em::DeviceStatusReportRequest* device_status, DevicePolicyResponseDelegate* delegate) { UMA_HISTOGRAM_ENUMERATION(kMetricPolicy, kMetricPolicyFetchRequested, kMetricPolicySize); AddJob(new DeviceManagementPolicyJob(this, device_management_token, device_id, UserAffiliationToString(affiliation), - request, delegate)); + request, device_status, delegate)); } void DeviceManagementBackendImpl::ProcessAutoEnrollmentRequest( diff --git a/chrome/browser/policy/device_management_backend_impl.h b/chrome/browser/policy/device_management_backend_impl.h index 2848882..244aa30 100644 --- a/chrome/browser/policy/device_management_backend_impl.h +++ b/chrome/browser/policy/device_management_backend_impl.h @@ -76,6 +76,7 @@ class DeviceManagementBackendImpl : public DeviceManagementBackend { const std::string& device_id, CloudPolicyDataStore::UserAffiliation affiliation, const em::DevicePolicyRequest& request, + const em::DeviceStatusReportRequest* device_status, DevicePolicyResponseDelegate* response_delegate) OVERRIDE; virtual void ProcessAutoEnrollmentRequest( const std::string& device_id, diff --git a/chrome/browser/policy/device_management_service_browsertest.cc b/chrome/browser/policy/device_management_service_browsertest.cc index b0f1d59..7088e37 100644 --- a/chrome/browser/policy/device_management_service_browsertest.cc +++ b/chrome/browser/policy/device_management_service_browsertest.cc @@ -123,7 +123,7 @@ IN_PROC_BROWSER_TEST_F(DeviceManagementServiceIntegrationTest, setting_request->set_key(kChromeDevicePolicySettingKey); backend->ProcessPolicyRequest(token_, "testid", CloudPolicyDataStore::USER_AFFILIATION_NONE, - request, &delegate); + request, NULL, &delegate); MessageLoop::current()->Run(); } @@ -190,7 +190,7 @@ IN_PROC_BROWSER_TEST_F(DeviceManagementServiceIntegrationTest, fetch_request->set_policy_type(kChromeUserPolicyType); backend->ProcessPolicyRequest(token_, "testid", CloudPolicyDataStore::USER_AFFILIATION_NONE, - request, &delegate); + request, NULL, &delegate); MessageLoop::current()->Run(); } diff --git a/chrome/browser/policy/device_management_service_unittest.cc b/chrome/browser/policy/device_management_service_unittest.cc index 9af4a0a..1682dd2 100644 --- a/chrome/browser/policy/device_management_service_unittest.cc +++ b/chrome/browser/policy/device_management_service_unittest.cc @@ -154,7 +154,7 @@ TEST_P(DeviceManagementServiceFailedRequestTest, PolicyRequest) { setting_request->set_key(kChromeDevicePolicySettingKey); backend_->ProcessPolicyRequest(kDMToken, kDeviceId, CloudPolicyDataStore::USER_AFFILIATION_NONE, - request, &mock); + request, NULL, &mock); TestURLFetcher* fetcher = factory_.GetFetcherByID(0); ASSERT_TRUE(fetcher); @@ -448,7 +448,7 @@ TEST_F(DeviceManagementServiceTest, CancelPolicyRequest) { setting_request->set_watermark("stale"); backend_->ProcessPolicyRequest(kDMToken, kDeviceId, CloudPolicyDataStore::USER_AFFILIATION_NONE, - request, &mock); + request, NULL, &mock); TestURLFetcher* fetcher = factory_.GetFetcherByID(0); ASSERT_TRUE(fetcher); @@ -501,7 +501,7 @@ TEST_F(DeviceManagementServiceTest, CancelRequestAfterShutdown) { setting_request->set_watermark("stale"); backend_->ProcessPolicyRequest(kDMToken, kDeviceId, CloudPolicyDataStore::USER_AFFILIATION_NONE, - request, &mock); + request, NULL, &mock); TestURLFetcher* fetcher = factory_.GetFetcherByID(0); ASSERT_TRUE(fetcher); diff --git a/chrome/browser/policy/device_status_collector.cc b/chrome/browser/policy/device_status_collector.cc new file mode 100644 index 0000000..4055b7d --- /dev/null +++ b/chrome/browser/policy/device_status_collector.cc @@ -0,0 +1,152 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/policy/device_status_collector.h" + +#include "base/bind.h" +#include "base/callback.h" +#include "base/message_loop.h" +#include "base/string_number_conversions.h" +#include "base/task.h" +#include "base/time.h" +#include "chrome/browser/idle.h" +#include "chrome/browser/policy/proto/device_management_backend.pb.h" +#include "chrome/browser/prefs/pref_service.h" +#include "chrome/browser/prefs/scoped_user_pref_update.h" + +using base::Time; +using base::TimeDelta; + +namespace em = enterprise_management; + +namespace { +// How many seconds of inactivity triggers the idle state. +const unsigned int kIdleStateThresholdSeconds = 300; + +// The maximum number of time periods stored in the local state. +const unsigned int kMaxStoredActivePeriods = 500; + +// Stores the baseline timestamp, to which the active periods are relative. +const char* const kPrefBaselineTime = "device_status.baseline_timestamp"; + +// Stores a list of timestamps representing device active periods. +const char* const kPrefDeviceActivePeriods = "device_status.active_periods"; + +bool GetTimestamp(const ListValue* list, int index, int64* out_value) { + std::string string_value; + if (list->GetString(index, &string_value)) + return base::StringToInt64(string_value, out_value); + return false; +} + +} // namespace + +namespace policy { + +DeviceStatusCollector::DeviceStatusCollector(PrefService* local_state) + : max_stored_active_periods_(kMaxStoredActivePeriods), + local_state_(local_state), + last_idle_check_(Time()), + last_idle_state_(IDLE_STATE_UNKNOWN) { + timer_.Start(FROM_HERE, + TimeDelta::FromSeconds( + DeviceStatusCollector::kPollIntervalSeconds), + this, &DeviceStatusCollector::CheckIdleState); +} + +DeviceStatusCollector::~DeviceStatusCollector() { +} + +// static +void DeviceStatusCollector::RegisterPrefs(PrefService* local_state) { + local_state->RegisterInt64Pref(kPrefBaselineTime, Time::Now().ToTimeT()); + local_state->RegisterListPref(kPrefDeviceActivePeriods, new ListValue); +} + +void DeviceStatusCollector::CheckIdleState() { + CalculateIdleState(kIdleStateThresholdSeconds, + base::Bind(&DeviceStatusCollector::IdleStateCallback, + base::Unretained(this))); +} + +Time DeviceStatusCollector::GetCurrentTime() { + return Time::Now(); +} + +void DeviceStatusCollector::AddActivePeriod(Time start, Time end) { + // Maintain the list of active periods in a local_state pref. + ListPrefUpdate update(local_state_, kPrefDeviceActivePeriods); + ListValue* active_periods = update.Get(); + + // Cap the number of active periods that we store. + if (active_periods->GetSize() >= 2 * max_stored_active_periods_) + return; + + Time epoch = Time::UnixEpoch(); + int64 start_timestamp = (start - epoch).InMilliseconds(); + Value* end_value = new StringValue( + base::Int64ToString((end - epoch).InMilliseconds())); + + int list_size = active_periods->GetSize(); + DCHECK(list_size % 2 == 0); + + // Check if this period can be combined with the previous one. + if (list_size > 0 && last_idle_state_ == IDLE_STATE_ACTIVE) { + int64 last_period_end; + if (GetTimestamp(active_periods, list_size - 1, &last_period_end) && + last_period_end == start_timestamp) { + active_periods->Set(list_size - 1, end_value); + return; + } + } + // Add a new period to the list. + active_periods->Append( + new StringValue(base::Int64ToString(start_timestamp))); + active_periods->Append(end_value); +} + +void DeviceStatusCollector::IdleStateCallback(IdleState state) { + Time now = GetCurrentTime(); + + if (state == IDLE_STATE_ACTIVE) { + unsigned int poll_interval = DeviceStatusCollector::kPollIntervalSeconds; + + // If it's been too long since the last report, assume that the system was + // in standby, and only count a single interval of activity. + if ((now - last_idle_check_).InSeconds() >= (2 * poll_interval)) + AddActivePeriod(now - TimeDelta::FromSeconds(poll_interval), now); + else + AddActivePeriod(last_idle_check_, now); + } + last_idle_check_ = now; + last_idle_state_ = state; +} + +void DeviceStatusCollector::GetStatus(em::DeviceStatusReportRequest* request) { + const ListValue* active_periods = + local_state_->GetList(kPrefDeviceActivePeriods); + em::TimePeriod* time_period; + + DCHECK(active_periods->GetSize() % 2 == 0); + + int period_count = active_periods->GetSize() / 2; + for (int i = 0; i < period_count; i++) { + int64 start, end; + + if (!GetTimestamp(active_periods, 2 * i, &start) || + !GetTimestamp(active_periods, 2 * i + 1, &end) || + end < start) { + // Something is amiss -- bail out. + NOTREACHED(); + break; + } + time_period = request->add_active_time(); + time_period->set_start_timestamp(start); + time_period->set_end_timestamp(end); + } + ListPrefUpdate update(local_state_, kPrefDeviceActivePeriods); + update.Get()->Clear(); +} + +} // namespace policy diff --git a/chrome/browser/policy/device_status_collector.h b/chrome/browser/policy/device_status_collector.h new file mode 100644 index 0000000..cef45ff --- /dev/null +++ b/chrome/browser/policy/device_status_collector.h @@ -0,0 +1,70 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_POLICY_DEVICE_STATUS_COLLECTOR_H_ +#define CHROME_BROWSER_POLICY_DEVICE_STATUS_COLLECTOR_H_ +#pragma once + +#include "base/time.h" +#include "base/timer.h" +#include "chrome/browser/idle.h" + +using base::Time; + +namespace enterprise_management { +class DeviceStatusReportRequest; +} + +class PrefService; + +namespace policy { + +// Collects and summarizes the status of an enterprised-managed ChromeOS device. +class DeviceStatusCollector { + public: + explicit DeviceStatusCollector(PrefService* local_state); + virtual ~DeviceStatusCollector(); + + void GetStatus(enterprise_management::DeviceStatusReportRequest* request); + + static void RegisterPrefs(PrefService* local_state); + + // How often, in seconds, to poll to see if the user is idle. + static const unsigned int kPollIntervalSeconds = 30; + + protected: + // Check whether the user has been idle for a certain period of time. + virtual void CheckIdleState(); + + // Used instead of Time::Now(), to make testing possible. + virtual Time GetCurrentTime(); + + // Callback which receives the results of the idle state check. + void IdleStateCallback(IdleState state); + + // The maximum number of active periods timestamps to be stored. + unsigned int max_stored_active_periods_; + + private: + void AddActivePeriod(base::Time start, base::Time end); + + // How often to poll to see if the user is idle. + int poll_interval_seconds_; + + PrefService* local_state_; + + // The last time an idle state check was performed. + Time last_idle_check_; + + // The idle state the last time it was checked. + IdleState last_idle_state_; + + base::RepeatingTimer<DeviceStatusCollector> timer_; + + DISALLOW_COPY_AND_ASSIGN(DeviceStatusCollector); +}; + +} // namespace policy + +#endif // CHROME_BROWSER_POLICY_DEVICE_STATUS_COLLECTOR_H_ diff --git a/chrome/browser/policy/device_status_collector_unittest.cc b/chrome/browser/policy/device_status_collector_unittest.cc new file mode 100644 index 0000000..8bda4b8 --- /dev/null +++ b/chrome/browser/policy/device_status_collector_unittest.cc @@ -0,0 +1,229 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/policy/device_status_collector.h" + +#include "base/message_loop.h" +#include "base/time.h" +#include "chrome/browser/idle.h" +#include "chrome/browser/policy/proto/device_management_backend.pb.h" +#include "chrome/browser/prefs/pref_service.h" +#include "chrome/test/base/testing_pref_service.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::TimeDelta; +using base::Time; + +namespace em = enterprise_management; + +namespace { + +class TestingDeviceStatusCollector : public policy::DeviceStatusCollector { + public: + explicit TestingDeviceStatusCollector(PrefService* local_state) + : policy::DeviceStatusCollector(local_state), + local_state_(local_state), + baseline_time_(Time::Now()) { + } + + void Simulate(IdleState* states, int len) { + for (int i = 0; i < len; i++) + IdleStateCallback(states[i]); + } + + void SimulateWithSleep(IdleState* states, int len, int ) { + for (int i = 0; i < len; i++) + IdleStateCallback(states[i]); + } + + void set_max_stored_active_periods(unsigned int value) { + max_stored_active_periods_ = value; + } + + protected: + virtual void CheckIdleState() OVERRIDE { + // This should never be called in testing, as it results in a dbus call. + NOTREACHED(); + } + + // Each time this is called, returns a time that is a fixed increment + // later than the previous time. + virtual Time GetCurrentTime() OVERRIDE { + static int call_count = 0; + return baseline_time_ + TimeDelta::FromSeconds( + policy::DeviceStatusCollector::kPollIntervalSeconds * call_count++); + } + + private: + PrefService* local_state_; + + // Baseline time for the fake times returned from GetCurrentTime(). + // It doesn't really matter what this is, as long as it stays the same over + // the lifetime of the object. + Time baseline_time_; +}; + +// Return the total number of active milliseconds contained in a device +// status report. +int64 GetActiveMilliseconds(em::DeviceStatusReportRequest& status) { + int64 active_milliseconds = 0; + for (int i = 0; i < status.active_time_size(); i++) { + const em::TimePeriod& period = status.active_time(i); + active_milliseconds += period.end_timestamp() - period.start_timestamp(); + } + return active_milliseconds; +} + +} // namespace + +namespace policy { + +class DeviceStatusCollectorTest : public testing::Test { + public: + DeviceStatusCollectorTest() + : message_loop_(new MessageLoop), + prefs_(), + status_collector_(&prefs_) { + DeviceStatusCollector::RegisterPrefs(&prefs_); + } + + protected: + // Convenience method. + int64 ActivePeriodMilliseconds() { + return policy::DeviceStatusCollector::kPollIntervalSeconds * 1000; + } + + scoped_ptr<MessageLoop> message_loop_; + + TestingPrefService prefs_; + TestingDeviceStatusCollector status_collector_; + em::DeviceStatusReportRequest status_; +}; + +TEST_F(DeviceStatusCollectorTest, AllIdle) { + IdleState test_states[] = { + IDLE_STATE_IDLE, + IDLE_STATE_IDLE, + IDLE_STATE_IDLE + }; + // Test reporting with no data. + status_collector_.GetStatus(&status_); + EXPECT_EQ(0, status_.active_time_size()); + EXPECT_EQ(0, GetActiveMilliseconds(status_)); + + // Test reporting with a single idle sample. + status_collector_.Simulate(test_states, 1); + status_collector_.GetStatus(&status_); + EXPECT_EQ(0, status_.active_time_size()); + EXPECT_EQ(0, GetActiveMilliseconds(status_)); + + // Test reporting with multiple consecutive idle samples. + status_collector_.Simulate(test_states, + sizeof(test_states) / sizeof(IdleState)); + status_collector_.GetStatus(&status_); + EXPECT_EQ(0, status_.active_time_size()); + EXPECT_EQ(0, GetActiveMilliseconds(status_)); +} + +TEST_F(DeviceStatusCollectorTest, AllActive) { + IdleState test_states[] = { + IDLE_STATE_ACTIVE, + IDLE_STATE_ACTIVE, + IDLE_STATE_ACTIVE + }; + // Test a single active sample. + status_collector_.Simulate(test_states, 1); + status_collector_.GetStatus(&status_); + EXPECT_EQ(1, status_.active_time_size()); + EXPECT_EQ(1 * ActivePeriodMilliseconds(), GetActiveMilliseconds(status_)); + status_.clear_active_time(); // Clear the result protobuf. + + // Test multiple consecutive active samples -- they should be coalesced + // into a single active period. + status_collector_.Simulate(test_states, + sizeof(test_states) / sizeof(IdleState)); + status_collector_.GetStatus(&status_); + EXPECT_EQ(1, status_.active_time_size()); + EXPECT_EQ(3 * ActivePeriodMilliseconds(), GetActiveMilliseconds(status_)); +} + +TEST_F(DeviceStatusCollectorTest, MixedStates) { + IdleState test_states[] = { + IDLE_STATE_ACTIVE, + IDLE_STATE_IDLE, + IDLE_STATE_ACTIVE, + IDLE_STATE_ACTIVE, + IDLE_STATE_IDLE, + IDLE_STATE_IDLE, + IDLE_STATE_ACTIVE + }; + status_collector_.Simulate(test_states, + sizeof(test_states) / sizeof(IdleState)); + status_collector_.GetStatus(&status_); + EXPECT_EQ(3, status_.active_time_size()); + EXPECT_EQ(4 * ActivePeriodMilliseconds(), GetActiveMilliseconds(status_)); +} + +TEST_F(DeviceStatusCollectorTest, StateKeptInPref) { + IdleState test_states[] = { + IDLE_STATE_ACTIVE, + IDLE_STATE_IDLE, + IDLE_STATE_ACTIVE, + IDLE_STATE_ACTIVE, + IDLE_STATE_IDLE, + IDLE_STATE_IDLE + }; + status_collector_.Simulate(test_states, + sizeof(test_states) / sizeof(IdleState)); + + // Process the list a second time with a different collector. + // It should be able to count the active periods found by the first + // collector, because the results are stored in a pref. + TestingDeviceStatusCollector second_collector(&prefs_); + second_collector.Simulate(test_states, + sizeof(test_states) / sizeof(IdleState)); + + second_collector.GetStatus(&status_); + EXPECT_EQ(4, status_.active_time_size()); + EXPECT_EQ(6 * ActivePeriodMilliseconds(), GetActiveMilliseconds(status_)); +} + +TEST_F(DeviceStatusCollectorTest, Times) { + IdleState test_states[] = { + IDLE_STATE_ACTIVE, + IDLE_STATE_IDLE, + IDLE_STATE_ACTIVE, + IDLE_STATE_ACTIVE, + IDLE_STATE_IDLE, + IDLE_STATE_IDLE + }; + status_collector_.Simulate(test_states, + sizeof(test_states) / sizeof(IdleState)); + status_collector_.GetStatus(&status_); + EXPECT_EQ(2, status_.active_time_size()); + + EXPECT_EQ(3 * ActivePeriodMilliseconds(), GetActiveMilliseconds(status_)); +} + +TEST_F(DeviceStatusCollectorTest, MaxStoredPeriods) { + IdleState test_states[] = { + IDLE_STATE_ACTIVE, + IDLE_STATE_IDLE + }; + unsigned int max_periods = 10; + + status_collector_.set_max_stored_active_periods(max_periods); + + // Simulate 12 active periods. + for (int i = 0; i < 12; i++) { + status_collector_.Simulate(test_states, + sizeof(test_states) / sizeof(IdleState)); + } + + // Check that we don't exceed the max number of periods. + status_collector_.GetStatus(&status_); + EXPECT_EQ(static_cast<int>(max_periods), status_.active_time_size()); +} + +} // namespace policy diff --git a/chrome/browser/policy/enterprise_metrics_browsertest.cc b/chrome/browser/policy/enterprise_metrics_browsertest.cc index 71118ea..24c2f6a 100644 --- a/chrome/browser/policy/enterprise_metrics_browsertest.cc +++ b/chrome/browser/policy/enterprise_metrics_browsertest.cc @@ -81,7 +81,7 @@ class DeviceManagementBackendTestHelper { EXPECT_CALL(delegate, HandlePolicyResponse(_)).Times(AnyNumber()); backend_->ProcessPolicyRequest("token", "testid", CloudPolicyDataStore::USER_AFFILIATION_NONE, - request, &delegate); + request, NULL, &delegate); } void UnmockCreateBackend() { diff --git a/chrome/browser/policy/mock_device_management_backend.h b/chrome/browser/policy/mock_device_management_backend.h index de517cd..88058d4 100644 --- a/chrome/browser/policy/mock_device_management_backend.h +++ b/chrome/browser/policy/mock_device_management_backend.h @@ -42,11 +42,12 @@ class MockDeviceManagementBackend : public DeviceManagementBackend { const em::DeviceUnregisterRequest& request, DeviceUnregisterResponseDelegate* delegate)); - MOCK_METHOD5(ProcessPolicyRequest, void( + MOCK_METHOD6(ProcessPolicyRequest, void( const std::string& device_management_token, const std::string& device_id, CloudPolicyDataStore::UserAffiliation affiliation, const em::DevicePolicyRequest& request, + const em::DeviceStatusReportRequest* device_status, DevicePolicyResponseDelegate* delegate)); MOCK_METHOD3(ProcessAutoEnrollmentRequest, void( @@ -87,7 +88,7 @@ ACTION(MockDeviceManagementBackendSucceedSpdyCloudPolicy) { // implementing support for signature verification). fetch_response->set_policy_data_signature("TODO"); fetch_response->set_new_public_key("TODO"); - arg4->HandlePolicyResponse(response); + arg5->HandlePolicyResponse(response); } ACTION_P(MockDeviceManagementBackendFailRegister, error) { @@ -95,7 +96,7 @@ ACTION_P(MockDeviceManagementBackendFailRegister, error) { } ACTION_P(MockDeviceManagementBackendFailPolicy, error) { - arg4->OnError(error); + arg5->OnError(error); } } // namespace policy diff --git a/chrome/browser/policy/mock_device_management_service.cc b/chrome/browser/policy/mock_device_management_service.cc index 555c65e..80315ea 100644 --- a/chrome/browser/policy/mock_device_management_service.cc +++ b/chrome/browser/policy/mock_device_management_service.cc @@ -36,9 +36,11 @@ void ProxyDeviceManagementBackend::ProcessPolicyRequest( const std::string& device_id, CloudPolicyDataStore::UserAffiliation affiliation, const em::DevicePolicyRequest& request, + const em::DeviceStatusReportRequest* device_status, DevicePolicyResponseDelegate* delegate) { backend_->ProcessPolicyRequest(device_management_token, device_id, - affiliation, request, delegate); + affiliation, request, device_status, + delegate); } void ProxyDeviceManagementBackend::ProcessAutoEnrollmentRequest( diff --git a/chrome/browser/policy/mock_device_management_service.h b/chrome/browser/policy/mock_device_management_service.h index 786908d..b854330 100644 --- a/chrome/browser/policy/mock_device_management_service.h +++ b/chrome/browser/policy/mock_device_management_service.h @@ -41,6 +41,7 @@ class ProxyDeviceManagementBackend : public DeviceManagementBackend { const std::string& device_id, CloudPolicyDataStore::UserAffiliation affiliation, const em::DevicePolicyRequest& request, + const em::DeviceStatusReportRequest* device_status, DevicePolicyResponseDelegate* delegate) OVERRIDE; virtual void ProcessAutoEnrollmentRequest( const std::string& device_id, diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc index ed99909..e4dc19e 100644 --- a/chrome/browser/prefs/browser_prefs.cc +++ b/chrome/browser/prefs/browser_prefs.cc @@ -35,6 +35,7 @@ #include "chrome/browser/page_info_model.h" #include "chrome/browser/password_manager/password_manager.h" #include "chrome/browser/policy/cloud_policy_subsystem.h" +#include "chrome/browser/policy/device_status_collector.h" #include "chrome/browser/policy/url_blacklist_manager.h" #include "chrome/browser/prefs/incognito_mode_prefs.h" #include "chrome/browser/prefs/session_startup_pref.h" @@ -129,6 +130,7 @@ void RegisterLocalState(PrefService* local_state) { SSLConfigServiceManager::RegisterPrefs(local_state); #if defined(ENABLE_CONFIGURATION_POLICY) policy::CloudPolicySubsystem::RegisterPrefs(local_state); + policy::DeviceStatusCollector::RegisterPrefs(local_state); #endif ProfileInfoCache::RegisterPrefs(local_state); ProfileManager::RegisterPrefs(local_state); diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 28d9e8f..9eb40e0 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -1809,6 +1809,8 @@ 'browser/policy/device_policy_cache.h', 'browser/policy/device_token_fetcher.cc', 'browser/policy/device_token_fetcher.h', + 'browser/policy/device_status_collector.cc', + 'browser/policy/device_status_collector.h', 'browser/policy/enterprise_install_attributes.cc', 'browser/policy/enterprise_install_attributes.h', 'browser/policy/enterprise_metrics.cc', diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index 5dbf976..0b8ccf1 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -1530,6 +1530,7 @@ 'browser/policy/device_management_backend_mock.h', 'browser/policy/device_management_service_unittest.cc', 'browser/policy/device_policy_cache_unittest.cc', + 'browser/policy/device_status_collector_unittest.cc', 'browser/policy/device_token_fetcher_unittest.cc', 'browser/policy/enterprise_install_attributes_unittest.cc', 'browser/policy/file_based_policy_provider_unittest.cc', |