diff options
author | dubroy@chromium.org <dubroy@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-16 16:33:40 +0000 |
---|---|---|
committer | dubroy@chromium.org <dubroy@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-16 16:33:40 +0000 |
commit | 262ff6bcd7cf6fbaef3376bfa18d762b21556153 (patch) | |
tree | 6110a517ff6cf5ef3941a4b1890dd41eae3c4c15 | |
parent | 04f51f5808cb973d7910d936d35dde87d9fc9edf (diff) | |
download | chromium_src-262ff6bcd7cf6fbaef3376bfa18d762b21556153.zip chromium_src-262ff6bcd7cf6fbaef3376bfa18d762b21556153.tar.gz chromium_src-262ff6bcd7cf6fbaef3376bfa18d762b21556153.tar.bz2 |
Aggregate device activity, and report per-day activity in device status reports.
BUG=chromium-os:26372
TEST=Manual.
Review URL: http://codereview.chromium.org/9348105
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@122299 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/policy/device_status_collector.cc | 173 | ||||
-rw-r--r-- | chrome/browser/policy/device_status_collector.h | 17 | ||||
-rw-r--r-- | chrome/browser/policy/device_status_collector_unittest.cc | 124 | ||||
-rw-r--r-- | chrome/browser/policy/proto/device_management_backend.proto | 12 | ||||
-rw-r--r-- | chrome/common/pref_names.cc | 4 | ||||
-rw-r--r-- | chrome/common/pref_names.h | 1 |
6 files changed, 224 insertions, 107 deletions
diff --git a/chrome/browser/policy/device_status_collector.cc b/chrome/browser/policy/device_status_collector.cc index f4fb746..7017207 100644 --- a/chrome/browser/policy/device_status_collector.cc +++ b/chrome/browser/policy/device_status_collector.cc @@ -15,6 +15,7 @@ #include "chrome/browser/prefs/scoped_user_pref_update.h" #include "chrome/common/chrome_notification_types.h" #include "chrome/common/chrome_version_info.h" +#include "chrome/common/pref_names.h" using base::Time; using base::TimeDelta; @@ -26,17 +27,26 @@ 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 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; +// How many days in the past to store active periods for. +const unsigned int kMaxStoredPastActivityDays = 30; + +// How many days in the future to store active periods for. +const unsigned int kMaxStoredFutureActivityDays = 2; + +const int64 kMillisecondsPerDay = Time::kMicrosecondsPerDay / 1000; + +// Record device activity for the specified day into the given dictionary. +void AddDeviceActivity(DictionaryValue* activity_times, + Time day_midnight, + TimeDelta activity) { + DCHECK(activity.InMilliseconds() < kMillisecondsPerDay); + int64 day_start_timestamp = + (day_midnight - Time::UnixEpoch()).InMilliseconds(); + std::string day_key = base::Int64ToString(day_start_timestamp); + int previous_activity = 0; + activity_times->GetInteger(day_key, &previous_activity); + activity_times->SetInteger(day_key, + previous_activity + activity.InMilliseconds()); } } // namespace @@ -46,17 +56,16 @@ namespace policy { DeviceStatusCollector::DeviceStatusCollector( PrefService* local_state, chromeos::system::StatisticsProvider* provider) - : max_stored_active_periods_(kMaxStoredActivePeriods), + : max_stored_past_activity_days_(kMaxStoredPastActivityDays), + max_stored_future_activity_days_(kMaxStoredFutureActivityDays), local_state_(local_state), last_idle_check_(Time()), - last_idle_state_(IDLE_STATE_UNKNOWN), statistics_provider_(provider), report_version_info_(false), report_activity_times_(false), report_boot_mode_(false) { timer_.Start(FROM_HERE, - TimeDelta::FromSeconds( - DeviceStatusCollector::kPollIntervalSeconds), + TimeDelta::FromSeconds(kPollIntervalSeconds), this, &DeviceStatusCollector::CheckIdleState); cros_settings_ = chromeos::CrosSettings::Get(); @@ -91,7 +100,8 @@ DeviceStatusCollector::~DeviceStatusCollector() { // static void DeviceStatusCollector::RegisterPrefs(PrefService* local_state) { - local_state->RegisterListPref(kPrefDeviceActivePeriods, new ListValue); + local_state->RegisterDictionaryPref(prefs::kDeviceActivityTimes, + new DictionaryValue); } void DeviceStatusCollector::CheckIdleState() { @@ -122,36 +132,61 @@ Time DeviceStatusCollector::GetCurrentTime() { return Time::Now(); } +// Remove all out-of-range activity times from the local store. +void DeviceStatusCollector::PruneStoredActivityPeriods(Time base_time) { + const DictionaryValue* activity_times = + local_state_->GetDictionary(prefs::kDeviceActivityTimes); + if (activity_times->size() <= + max_stored_past_activity_days_ + max_stored_future_activity_days_) + return; + + Time min_time = + base_time - TimeDelta::FromDays(max_stored_past_activity_days_); + Time max_time = + base_time + TimeDelta::FromDays(max_stored_future_activity_days_); + const Time epoch = Time::UnixEpoch(); + + DictionaryValue* copy = activity_times->DeepCopy(); + for (DictionaryValue::key_iterator it = activity_times->begin_keys(); + it != activity_times->end_keys(); ++it) { + int64 timestamp; + + if (base::StringToInt64(*it, ×tamp)) { + // Remove data that is too old, or too far in the future. + Time day_midnight = epoch + TimeDelta::FromMilliseconds(timestamp); + if (day_midnight > min_time && day_midnight < max_time) + continue; + } + // The entry is out of range or couldn't be parsed. Remove it. + copy->Remove(*it, NULL); + } + local_state_->Set(prefs::kDeviceActivityTimes, *copy); +} + void DeviceStatusCollector::AddActivePeriod(Time start, Time end) { + DCHECK(start < end); + // Maintain the list of active periods in a local_state pref. - ListPrefUpdate update(local_state_, kPrefDeviceActivePeriods); - ListValue* active_periods = update.Get(); + DictionaryPrefUpdate update(local_state_, prefs::kDeviceActivityTimes); + DictionaryValue* activity_times = update.Get(); - // Cap the number of active periods that we store. - if (active_periods->GetSize() >= 2 * max_stored_active_periods_) - return; + Time midnight = end.LocalMidnight(); - 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; - } + // Figure out UTC midnight on the same day as the local day. + Time::Exploded exploded; + midnight.LocalExplode(&exploded); + Time utc_midnight = Time::FromUTCExploded(exploded); + + // Record the device activity for today. + TimeDelta activity_today = end - MAX(midnight, start); + AddDeviceActivity(activity_times, utc_midnight, activity_today); + + // If this interval spans two days, record activity for yesterday too. + if (start < midnight) { + AddDeviceActivity(activity_times, + utc_midnight - TimeDelta::FromDays(1), + midnight - start); } - // 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) { @@ -162,44 +197,46 @@ 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); + // If it's been too long since the last report, or if the activity is + // negative (which can happen when the clock changes), assume a single + // interval of activity. + int active_seconds = (now - last_idle_check_).InSeconds(); + if (active_seconds < 0 || + active_seconds >= static_cast<int>((2 * kPollIntervalSeconds))) + AddActivePeriod(now - TimeDelta::FromSeconds(kPollIntervalSeconds), now); else AddActivePeriod(last_idle_check_, now); + + PruneStoredActivityPeriods(now); } last_idle_check_ = now; - last_idle_state_ = state; } void DeviceStatusCollector::GetActivityTimes( 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. + DictionaryPrefUpdate update(local_state_, prefs::kDeviceActivityTimes); + DictionaryValue* activity_times = update.Get(); + + for (DictionaryValue::key_iterator it = activity_times->begin_keys(); + it != activity_times->end_keys(); ++it) { + int64 start_timestamp; + int activity_milliseconds; + if (base::StringToInt64(*it, &start_timestamp) && + activity_times->GetInteger(*it, &activity_milliseconds)) { + // This is correct even when there are leap seconds, because when a leap + // second occurs, two consecutive seconds have the same timestamp. + int64 end_timestamp = start_timestamp + kMillisecondsPerDay; + + em::ActiveTimePeriod* active_period = request->add_active_period(); + em::TimePeriod* period = active_period->mutable_time_period(); + period->set_start_timestamp(start_timestamp); + period->set_end_timestamp(end_timestamp); + active_period->set_active_duration(activity_milliseconds); + } else { 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(); + activity_times->Clear(); } void DeviceStatusCollector::GetVersionInfo( diff --git a/chrome/browser/policy/device_status_collector.h b/chrome/browser/policy/device_status_collector.h index 1d6d1f0..4afffcb 100644 --- a/chrome/browser/policy/device_status_collector.h +++ b/chrome/browser/policy/device_status_collector.h @@ -51,10 +51,20 @@ class DeviceStatusCollector : public content::NotificationObserver { // 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_; + // The number of days in the past to store device activity. + // This is kept in case device status uploads fail for a number of days. + unsigned int max_stored_past_activity_days_; + + // The number of days in the future to store device activity. + // When changing the system time and/or timezones, it's possible to record + // activity time that is slightly in the future. + unsigned int max_stored_future_activity_days_; private: + // Prevents the local store of activity periods from growing too large by + // removing entries that are outside the reporting window. + void PruneStoredActivityPeriods(base::Time base_time); + void AddActivePeriod(base::Time start, base::Time end); // Callbacks from chromeos::VersionLoader. @@ -88,9 +98,6 @@ class DeviceStatusCollector : public content::NotificationObserver { // The last time an idle state check was performed. base::Time last_idle_check_; - // The idle state the last time it was checked. - IdleState last_idle_state_; - base::RepeatingTimer<DeviceStatusCollector> timer_; chromeos::VersionLoader version_loader_; diff --git a/chrome/browser/policy/device_status_collector_unittest.cc b/chrome/browser/policy/device_status_collector_unittest.cc index 96a77ed..3dcfb3f 100644 --- a/chrome/browser/policy/device_status_collector_unittest.cc +++ b/chrome/browser/policy/device_status_collector_unittest.cc @@ -26,14 +26,18 @@ namespace em = enterprise_management; namespace { +const int64 kMillisecondsPerDay = Time::kMicrosecondsPerDay / 1000; + class TestingDeviceStatusCollector : public policy::DeviceStatusCollector { public: TestingDeviceStatusCollector( PrefService* local_state, chromeos::system::StatisticsProvider* provider) : policy::DeviceStatusCollector(local_state, provider), - local_state_(local_state), - baseline_time_(Time::Now()) { + local_state_(local_state) { + // Set the baseline time to a fixed value (1 AM) to prevent test flakiness + // due to a single activity period spanning two days. + SetBaselineTime(Time::Now().LocalMidnight() + TimeDelta::FromHours(1)); } void Simulate(IdleState* states, int len) { @@ -41,13 +45,18 @@ class TestingDeviceStatusCollector : public policy::DeviceStatusCollector { IdleStateCallback(states[i]); } - void SimulateWithSleep(IdleState* states, int len, int ) { - for (int i = 0; i < len; i++) - IdleStateCallback(states[i]); + void set_max_stored_past_activity_days(unsigned int value) { + max_stored_past_activity_days_ = value; + } + + void set_max_stored_future_activity_days(unsigned int value) { + max_stored_future_activity_days_ = value; } - void set_max_stored_active_periods(unsigned int value) { - max_stored_active_periods_ = value; + // Reset the baseline time. + void SetBaselineTime(Time time) { + baseline_time_ = time; + baseline_offset_periods_ = 0; } protected: @@ -59,27 +68,27 @@ class TestingDeviceStatusCollector : public policy::DeviceStatusCollector { // 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++); + int poll_interval = policy::DeviceStatusCollector::kPollIntervalSeconds; + return baseline_time_ + + TimeDelta::FromSeconds(poll_interval * baseline_offset_periods_++); } 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_; + + // The number of simulated periods since the baseline time. + int baseline_offset_periods_; }; // 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(); + for (int i = 0; i < status.active_period_size(); i++) { + active_milliseconds += status.active_period(i).active_duration(); } return active_milliseconds; } @@ -152,20 +161,20 @@ TEST_F(DeviceStatusCollectorTest, AllIdle) { // Test reporting with no data. status_collector_.GetStatus(&status_); - EXPECT_EQ(0, status_.active_time_size()); + EXPECT_EQ(0, status_.active_period_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, status_.active_period_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, status_.active_period_size()); EXPECT_EQ(0, GetActiveMilliseconds(status_)); } @@ -180,16 +189,15 @@ TEST_F(DeviceStatusCollectorTest, AllActive) { // 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, status_.active_period_size()); EXPECT_EQ(1 * ActivePeriodMilliseconds(), GetActiveMilliseconds(status_)); - status_.clear_active_time(); // Clear the result protobuf. + status_.clear_active_period(); // Clear the result protobuf. - // Test multiple consecutive active samples -- they should be coalesced - // into a single active period. + // Test multiple consecutive active samples. status_collector_.Simulate(test_states, sizeof(test_states) / sizeof(IdleState)); status_collector_.GetStatus(&status_); - EXPECT_EQ(1, status_.active_time_size()); + EXPECT_EQ(1, status_.active_period_size()); EXPECT_EQ(3 * ActivePeriodMilliseconds(), GetActiveMilliseconds(status_)); } @@ -207,7 +215,6 @@ TEST_F(DeviceStatusCollectorTest, MixedStates) { 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_)); } @@ -233,7 +240,6 @@ TEST_F(DeviceStatusCollectorTest, StateKeptInPref) { sizeof(test_states) / sizeof(IdleState)); second_collector.GetStatus(&status_); - EXPECT_EQ(4, status_.active_time_size()); EXPECT_EQ(6 * ActivePeriodMilliseconds(), GetActiveMilliseconds(status_)); } @@ -250,8 +256,6 @@ TEST_F(DeviceStatusCollectorTest, Times) { 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_)); } @@ -260,20 +264,45 @@ TEST_F(DeviceStatusCollectorTest, MaxStoredPeriods) { IDLE_STATE_ACTIVE, IDLE_STATE_IDLE }; - unsigned int max_periods = 10; + unsigned int max_days = 10; cros_settings_->SetBoolean(chromeos::kReportDeviceActivityTimes, true); - status_collector_.set_max_stored_active_periods(max_periods); + status_collector_.set_max_stored_past_activity_days(max_days - 1); + status_collector_.set_max_stored_future_activity_days(1); + Time baseline = Time::Now().LocalMidnight(); // Simulate 12 active periods. - for (int i = 0; i < 12; i++) { + for (int i = 0; i < static_cast<int>(max_days) + 2; i++) { status_collector_.Simulate(test_states, sizeof(test_states) / sizeof(IdleState)); + // Advance the simulated clock by a day. + baseline += TimeDelta::FromDays(1); + status_collector_.SetBaselineTime(baseline); } // 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()); + EXPECT_EQ(static_cast<int>(max_days), status_.active_period_size()); + + // Simulate some future times. + for (int i = 0; i < static_cast<int>(max_days) + 2; i++) { + status_collector_.Simulate(test_states, + sizeof(test_states) / sizeof(IdleState)); + // Advance the simulated clock by a day. + baseline += TimeDelta::FromDays(1); + status_collector_.SetBaselineTime(baseline); + } + // Set the clock back so the previous simulated times are in the future. + baseline -= TimeDelta::FromDays(20); + status_collector_.SetBaselineTime(baseline); + + // Collect one more data point to trigger pruning. + status_collector_.Simulate(test_states, 1); + + // Check that we don't exceed the max number of periods. + status_.clear_active_period(); + status_collector_.GetStatus(&status_); + EXPECT_LT(status_.active_period_size(), static_cast<int>(max_days)); } TEST_F(DeviceStatusCollectorTest, ActivityTimesDisabledByDefault) { @@ -288,10 +317,41 @@ TEST_F(DeviceStatusCollectorTest, ActivityTimesDisabledByDefault) { status_collector_.Simulate(test_states, sizeof(test_states) / sizeof(IdleState)); status_collector_.GetStatus(&status_); - EXPECT_EQ(0, status_.active_time_size()); + EXPECT_EQ(0, status_.active_period_size()); EXPECT_EQ(0, GetActiveMilliseconds(status_)); } +TEST_F(DeviceStatusCollectorTest, ActivityCrossingMidnight) { + IdleState test_states[] = { + IDLE_STATE_ACTIVE + }; + cros_settings_->SetBoolean(chromeos::kReportDeviceActivityTimes, true); + + // Set the baseline time to 10 seconds after midnight. + status_collector_.SetBaselineTime( + Time::Now().LocalMidnight() + TimeDelta::FromSeconds(10)); + + status_collector_.Simulate(test_states, 1); + status_collector_.GetStatus(&status_); + ASSERT_EQ(2, status_.active_period_size()); + + em::ActiveTimePeriod period0 = status_.active_period(0); + em::ActiveTimePeriod period1 = status_.active_period(1); + EXPECT_EQ(ActivePeriodMilliseconds() - 10000, period0.active_duration()); + EXPECT_EQ(10000, period1.active_duration()); + + em::TimePeriod time_period0 = period0.time_period(); + em::TimePeriod time_period1 = period1.time_period(); + + EXPECT_EQ(time_period0.end_timestamp(), time_period1.start_timestamp()); + + // Ensure that the start and end times for the period are a day apart. + EXPECT_EQ(time_period0.end_timestamp() - time_period0.start_timestamp(), + kMillisecondsPerDay); + EXPECT_EQ(time_period1.end_timestamp() - time_period1.start_timestamp(), + kMillisecondsPerDay); +} + TEST_F(DeviceStatusCollectorTest, DevSwitchBootMode) { // Test that boot mode data is not reported if the pref is not turned on. status_collector_.GetStatus(&status_); diff --git a/chrome/browser/policy/proto/device_management_backend.proto b/chrome/browser/policy/proto/device_management_backend.proto index eb31ebf..f5da7f7c 100644 --- a/chrome/browser/policy/proto/device_management_backend.proto +++ b/chrome/browser/policy/proto/device_management_backend.proto @@ -229,6 +229,11 @@ message TimePeriod { optional int64 end_timestamp = 2; } +message ActiveTimePeriod { + optional TimePeriod time_period = 1; + optional int32 active_duration = 2; +} + // This captures launch events for one app/extension or other installments. message InstallableLaunch { optional string install_id = 1; @@ -254,11 +259,14 @@ message DeviceStatusReportRequest { // If the mode is unknown, this field should not be set. optional string boot_mode = 3; - // Device active times collection since last report rpc call. - repeated TimePeriod active_time = 4; + // No longer used -- use active_period instead. + repeated TimePeriod active_time = 4 [deprecated=true]; // The browser version string as shown in the About dialog. optional string browser_version = 5; + + // A list of periods when the device was active, aggregated by day. + repeated ActiveTimePeriod active_period = 6; } // Report session (a user on one device) level status. diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc index e91a9a6..a71bbcd 100644 --- a/chrome/common/pref_names.cc +++ b/chrome/common/pref_names.cc @@ -1607,6 +1607,10 @@ const char kReportDeviceVersionInfo[] = "device_status.report_version_info"; // A boolean pref that indicates whether device activity times should be // recorded and reported along with device policy requests. const char kReportDeviceActivityTimes[] = "device_status.report_activity_times"; + +// The local state pref that stores device activity times before reporting +// them to the policy server. +extern const char kDeviceActivityTimes[] = "device_status.activity_times"; #endif // Whether there is a Flash version installed that supports clearing LSO data. diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h index f3374b3..396c649 100644 --- a/chrome/common/pref_names.h +++ b/chrome/common/pref_names.h @@ -617,6 +617,7 @@ extern const char kShouldAutoEnroll[]; extern const char kAutoEnrollmentPowerLimit[]; extern const char kReportDeviceVersionInfo[]; extern const char kReportDeviceActivityTimes[]; +extern const char kDeviceActivityTimes[]; #endif extern const char kClearPluginLSODataEnabled[]; |