Commit 07318f0b authored by Polina Bondarenko's avatar Polina Bondarenko Committed by Commit Bot

arc: DeviceArcDataSnapshotHours policy impl.

Add SnapshotHoursPolicyService to handle policy changes.

BUG=b:170187468
TEST=components_unittests

Change-Id: I1918d55d6e53b0085473f7bf650cf949767ed4b8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2514148Reviewed-by: default avatarRoman Sorokin [CET] <rsorokin@chromium.org>
Reviewed-by: default avatarSergey Poromov <poromov@chromium.org>
Reviewed-by: default avatarYusuke Sato <yusukes@chromium.org>
Commit-Queue: Polina Bondarenko <pbond@chromium.org>
Auto-Submit: Polina Bondarenko <pbond@chromium.org>
Cr-Commit-Position: refs/heads/master@{#825794}
parent f397a0d8
...@@ -12,6 +12,7 @@ namespace em = enterprise_management; ...@@ -12,6 +12,7 @@ namespace em = enterprise_management;
namespace policy { namespace policy {
namespace { namespace {
constexpr base::TimeDelta kWeek = base::TimeDelta::FromDays(7); constexpr base::TimeDelta kWeek = base::TimeDelta::FromDays(7);
constexpr base::TimeDelta kDay = base::TimeDelta::FromDays(1); constexpr base::TimeDelta kDay = base::TimeDelta::FromDays(1);
constexpr base::TimeDelta kHour = base::TimeDelta::FromHours(1); constexpr base::TimeDelta kHour = base::TimeDelta::FromHours(1);
...@@ -31,6 +32,11 @@ WeeklyTime GetWeeklyTimeFromExploded( ...@@ -31,6 +32,11 @@ WeeklyTime GetWeeklyTimeFromExploded(
} // namespace } // namespace
// static
const char WeeklyTime::kDayOfWeek[] = "day_of_week";
const char WeeklyTime::kTime[] = "time";
const char WeeklyTime::kTimezoneOffset[] = "timezon_offset";
WeeklyTime::WeeklyTime(int day_of_week, WeeklyTime::WeeklyTime(int day_of_week,
int milliseconds, int milliseconds,
base::Optional<int> timezone_offset) base::Optional<int> timezone_offset)
...@@ -49,10 +55,10 @@ WeeklyTime& WeeklyTime::operator=(const WeeklyTime& rhs) = default; ...@@ -49,10 +55,10 @@ WeeklyTime& WeeklyTime::operator=(const WeeklyTime& rhs) = default;
std::unique_ptr<base::DictionaryValue> WeeklyTime::ToValue() const { std::unique_ptr<base::DictionaryValue> WeeklyTime::ToValue() const {
auto weekly_time = std::make_unique<base::DictionaryValue>(); auto weekly_time = std::make_unique<base::DictionaryValue>();
weekly_time->SetInteger("day_of_week", day_of_week_); weekly_time->SetInteger(kDayOfWeek, day_of_week_);
weekly_time->SetInteger("time", milliseconds_); weekly_time->SetInteger(kTime, milliseconds_);
if (timezone_offset_) if (timezone_offset_)
weekly_time->SetInteger("timezone_offset", timezone_offset_.value()); weekly_time->SetInteger(kTimezoneOffset, timezone_offset_.value());
return weekly_time; return weekly_time;
} }
...@@ -137,4 +143,35 @@ std::unique_ptr<WeeklyTime> WeeklyTime::ExtractFromProto( ...@@ -137,4 +143,35 @@ std::unique_ptr<WeeklyTime> WeeklyTime::ExtractFromProto(
timezone_offset); timezone_offset);
} }
// static
std::unique_ptr<WeeklyTime> WeeklyTime::ExtractFromValue(
const base::Value* value,
base::Optional<int> timezone_offset) {
if (!value) {
LOG(ERROR) << "Passed nullptr value.";
return nullptr;
}
auto day_of_week = value->FindIntKey(kDayOfWeek);
if (!day_of_week.has_value() || day_of_week.value() < 1 ||
day_of_week.value() > 7) {
LOG(ERROR) << "Day of week is absent or invalid";
return nullptr;
}
auto time_of_day = value->FindIntKey(kTime);
if (!time_of_day.has_value()) {
LOG(ERROR) << "Time is absent";
return nullptr;
}
if (!(time_of_day.value() >= 0 &&
time_of_day.value() < kDay.InMilliseconds())) {
LOG(ERROR) << "Invalid time value: " << time_of_day.value()
<< ", the value should be in [0; " << kDay.InMilliseconds()
<< ").";
return nullptr;
}
return std::make_unique<WeeklyTime>(day_of_week.value(), time_of_day.value(),
timezone_offset);
}
} // namespace policy } // namespace policy
...@@ -21,6 +21,11 @@ namespace policy { ...@@ -21,6 +21,11 @@ namespace policy {
// beginning of the day. // beginning of the day.
class CHROMEOS_EXPORT WeeklyTime { class CHROMEOS_EXPORT WeeklyTime {
public: public:
// Dictionary value key constants for testing.
static const char kDayOfWeek[];
static const char kTime[];
static const char kTimezoneOffset[];
WeeklyTime(int day_of_week, WeeklyTime(int day_of_week,
int milliseconds, int milliseconds,
base::Optional<int> timezone_offset); base::Optional<int> timezone_offset);
...@@ -77,6 +82,16 @@ class CHROMEOS_EXPORT WeeklyTime { ...@@ -77,6 +82,16 @@ class CHROMEOS_EXPORT WeeklyTime {
const enterprise_management::WeeklyTimeProto& container, const enterprise_management::WeeklyTimeProto& container,
base::Optional<int> timezone_offset); base::Optional<int> timezone_offset);
// Return WeeklyTime structure from Value in format:
// { "day_of_week" : int # value is from 1 to 7 (1 = Monday, 2 = Tuesday,
// etc.)
// "time" : int # in milliseconds from the beginning of the day.
// }.
// Return nullptr if WeeklyTime structure isn't correct.
static std::unique_ptr<WeeklyTime> ExtractFromValue(
const base::Value* value,
base::Optional<int> timezone_offset);
// Return the current time in GMT in WeeklyTime structure. // Return the current time in GMT in WeeklyTime structure.
static WeeklyTime GetCurrentGmtWeeklyTime(base::Clock* clock); static WeeklyTime GetCurrentGmtWeeklyTime(base::Clock* clock);
......
...@@ -11,6 +11,10 @@ namespace em = enterprise_management; ...@@ -11,6 +11,10 @@ namespace em = enterprise_management;
namespace policy { namespace policy {
// static
const char WeeklyTimeInterval::kStart[] = "start";
const char WeeklyTimeInterval::kEnd[] = "end";
WeeklyTimeInterval::WeeklyTimeInterval(const WeeklyTime& start, WeeklyTimeInterval::WeeklyTimeInterval(const WeeklyTime& start,
const WeeklyTime& end) const WeeklyTime& end)
: start_(start), end_(end) { : start_(start), end_(end) {
...@@ -25,8 +29,8 @@ WeeklyTimeInterval& WeeklyTimeInterval::operator=( ...@@ -25,8 +29,8 @@ WeeklyTimeInterval& WeeklyTimeInterval::operator=(
std::unique_ptr<base::DictionaryValue> WeeklyTimeInterval::ToValue() const { std::unique_ptr<base::DictionaryValue> WeeklyTimeInterval::ToValue() const {
auto interval = std::make_unique<base::DictionaryValue>(); auto interval = std::make_unique<base::DictionaryValue>();
interval->SetDictionary("start", start_.ToValue()); interval->SetDictionary(kStart, start_.ToValue());
interval->SetDictionary("end", end_.ToValue()); interval->SetDictionary(kEnd, end_.ToValue());
return interval; return interval;
} }
...@@ -54,4 +58,21 @@ std::unique_ptr<WeeklyTimeInterval> WeeklyTimeInterval::ExtractFromProto( ...@@ -54,4 +58,21 @@ std::unique_ptr<WeeklyTimeInterval> WeeklyTimeInterval::ExtractFromProto(
return std::make_unique<WeeklyTimeInterval>(*start, *end); return std::make_unique<WeeklyTimeInterval>(*start, *end);
} }
// static
std::unique_ptr<WeeklyTimeInterval> WeeklyTimeInterval::ExtractFromValue(
const base::Value* value,
base::Optional<int> timezone_offset) {
if (!value || !value->FindDictKey(kStart) || !value->FindDictKey(kEnd)) {
LOG(WARNING) << "Interval without start or/and end.";
return nullptr;
}
auto start =
WeeklyTime::ExtractFromValue(value->FindDictKey(kStart), timezone_offset);
auto end =
WeeklyTime::ExtractFromValue(value->FindDictKey(kEnd), timezone_offset);
if (!start || !end)
return nullptr;
return std::make_unique<WeeklyTimeInterval>(*start, *end);
}
} // namespace policy } // namespace policy
...@@ -21,6 +21,10 @@ namespace policy { ...@@ -21,6 +21,10 @@ namespace policy {
// Both WeeklyTimes need to have the same timezone_offset. // Both WeeklyTimes need to have the same timezone_offset.
class CHROMEOS_EXPORT WeeklyTimeInterval { class CHROMEOS_EXPORT WeeklyTimeInterval {
public: public:
// Dictionary value key constants for testing.
static const char kStart[];
static const char kEnd[];
WeeklyTimeInterval(const WeeklyTime& start, const WeeklyTime& end); WeeklyTimeInterval(const WeeklyTime& start, const WeeklyTime& end);
WeeklyTimeInterval(const WeeklyTimeInterval& rhs); WeeklyTimeInterval(const WeeklyTimeInterval& rhs);
...@@ -60,6 +64,20 @@ class CHROMEOS_EXPORT WeeklyTimeInterval { ...@@ -60,6 +64,20 @@ class CHROMEOS_EXPORT WeeklyTimeInterval {
const enterprise_management::WeeklyTimeIntervalProto& container, const enterprise_management::WeeklyTimeIntervalProto& container,
base::Optional<int> timezone_offset); base::Optional<int> timezone_offset);
// Return time interval made from Value in format:
// { "start" : WeeklyTime,
// "end" : WeeklyTime }
// WeeklyTime dictionary format:
// { "day_of_week" : int # value is from 1 to 7 (1 = Monday, 2 = Tuesday,
// etc.)
// "time" : int # in milliseconds from the beginning of the day.
// "timezone_offset" : int # in milliseconds, how much time ahead of GMT.
// }
// Return nullptr if value contains an invalid interval.
static std::unique_ptr<WeeklyTimeInterval> ExtractFromValue(
const base::Value* value,
base::Optional<int> timezone_offset);
WeeklyTime start() const { return start_; } WeeklyTime start() const { return start_; }
WeeklyTime end() const { return end_; } WeeklyTime end() const { return end_; }
......
...@@ -76,8 +76,10 @@ TEST_P(SingleWeeklyTimeIntervalTest, ToValue) { ...@@ -76,8 +76,10 @@ TEST_P(SingleWeeklyTimeIntervalTest, ToValue) {
WeeklyTimeInterval interval = WeeklyTimeInterval(start, end); WeeklyTimeInterval interval = WeeklyTimeInterval(start, end);
std::unique_ptr<base::DictionaryValue> interval_value = interval.ToValue(); std::unique_ptr<base::DictionaryValue> interval_value = interval.ToValue();
base::DictionaryValue expected_interval_value; base::DictionaryValue expected_interval_value;
expected_interval_value.SetDictionary("start", start.ToValue()); expected_interval_value.SetDictionary(WeeklyTimeInterval::kStart,
expected_interval_value.SetDictionary("end", end.ToValue()); start.ToValue());
expected_interval_value.SetDictionary(WeeklyTimeInterval::kEnd,
end.ToValue());
EXPECT_EQ(*interval_value, expected_interval_value); EXPECT_EQ(*interval_value, expected_interval_value);
} }
...@@ -146,6 +148,90 @@ TEST_P(SingleWeeklyTimeIntervalTest, ExtractFromProto_Valid) { ...@@ -146,6 +148,90 @@ TEST_P(SingleWeeklyTimeIntervalTest, ExtractFromProto_Valid) {
EXPECT_EQ(result->start().timezone_offset(), 0); EXPECT_EQ(result->start().timezone_offset(), 0);
} }
TEST_P(SingleWeeklyTimeIntervalTest, ExtractFromValue_Empty) {
base::DictionaryValue value;
auto result = WeeklyTimeInterval::ExtractFromValue(&value, 0);
ASSERT_FALSE(result);
}
TEST_P(SingleWeeklyTimeIntervalTest, ExtractFromValue_NoEnd) {
base::DictionaryValue value;
base::DictionaryValue start;
EXPECT_TRUE(
start.SetIntKey(WeeklyTime::kDayOfWeek, kWeekdays[start_day_of_week()]));
EXPECT_TRUE(start.SetIntKey(WeeklyTime::kTime, start_time()));
value.SetKey(WeeklyTimeInterval::kStart, std::move(start));
auto result = WeeklyTimeInterval::ExtractFromValue(&value, 0);
ASSERT_FALSE(result);
}
TEST_P(SingleWeeklyTimeIntervalTest, ExtractFromValue_NoStart) {
base::DictionaryValue value;
base::DictionaryValue end;
EXPECT_TRUE(
end.SetIntKey(WeeklyTime::kDayOfWeek, kWeekdays[end_day_of_week()]));
EXPECT_TRUE(end.SetIntKey(WeeklyTime::kTime, end_time()));
value.SetKey(WeeklyTimeInterval::kEnd, std::move(end));
auto result = WeeklyTimeInterval::ExtractFromValue(&value, 0);
ASSERT_FALSE(result);
}
TEST_P(SingleWeeklyTimeIntervalTest, ExtractFromValue_InvalidStart) {
base::DictionaryValue value;
base::DictionaryValue start;
EXPECT_TRUE(start.SetIntKey(WeeklyTime::kDayOfWeek, kWeekdays[0]));
EXPECT_TRUE(start.SetIntKey(WeeklyTime::kTime, start_time()));
value.SetKey(WeeklyTimeInterval::kStart, std::move(start));
base::DictionaryValue end;
EXPECT_TRUE(
end.SetIntKey(WeeklyTime::kDayOfWeek, kWeekdays[end_day_of_week()]));
EXPECT_TRUE(end.SetIntKey(WeeklyTime::kTime, end_time()));
value.SetKey(WeeklyTimeInterval::kEnd, std::move(end));
auto result = WeeklyTimeInterval::ExtractFromValue(&value, 0);
ASSERT_FALSE(result);
}
TEST_P(SingleWeeklyTimeIntervalTest, ExtractFromValue_InvalidEnd) {
base::DictionaryValue value;
base::DictionaryValue start;
EXPECT_TRUE(
start.SetIntKey(WeeklyTime::kDayOfWeek, kWeekdays[start_day_of_week()]));
EXPECT_TRUE(start.SetIntKey(WeeklyTime::kTime, start_time()));
value.SetKey(WeeklyTimeInterval::kStart, std::move(start));
base::DictionaryValue end;
EXPECT_TRUE(end.SetIntKey(WeeklyTime::kDayOfWeek, kWeekdays[0]));
EXPECT_TRUE(end.SetIntKey(WeeklyTime::kTime, end_time()));
value.SetKey(WeeklyTimeInterval::kEnd, std::move(end));
auto result = WeeklyTimeInterval::ExtractFromValue(&value, 0);
ASSERT_FALSE(result);
}
TEST_P(SingleWeeklyTimeIntervalTest, ExtractFromValue_Valid) {
base::DictionaryValue value;
base::DictionaryValue start;
EXPECT_TRUE(
start.SetIntKey(WeeklyTime::kDayOfWeek, kWeekdays[start_day_of_week()]));
EXPECT_TRUE(start.SetIntKey(WeeklyTime::kTime, start_time()));
value.SetKey(WeeklyTimeInterval::kStart, std::move(start));
base::DictionaryValue end;
EXPECT_TRUE(
end.SetIntKey(WeeklyTime::kDayOfWeek, kWeekdays[end_day_of_week()]));
EXPECT_TRUE(end.SetIntKey(WeeklyTime::kTime, end_time()));
value.SetKey(WeeklyTimeInterval::kEnd, std::move(end));
auto result = WeeklyTimeInterval::ExtractFromValue(&value, 0);
ASSERT_TRUE(result);
EXPECT_EQ(result->start().day_of_week(), start_day_of_week());
EXPECT_EQ(result->start().milliseconds(), start_time());
EXPECT_EQ(result->end().day_of_week(), end_day_of_week());
EXPECT_EQ(result->end().milliseconds(), end_time());
EXPECT_EQ(result->start().timezone_offset(), 0);
}
INSTANTIATE_TEST_SUITE_P(OneMinuteInterval, INSTANTIATE_TEST_SUITE_P(OneMinuteInterval,
SingleWeeklyTimeIntervalTest, SingleWeeklyTimeIntervalTest,
testing::Values(std::make_tuple(kWednesday, testing::Values(std::make_tuple(kWednesday,
......
...@@ -79,10 +79,11 @@ TEST_P(SingleWeeklyTimeTest, ToValue) { ...@@ -79,10 +79,11 @@ TEST_P(SingleWeeklyTimeTest, ToValue) {
std::unique_ptr<base::DictionaryValue> weekly_time_value = std::unique_ptr<base::DictionaryValue> weekly_time_value =
weekly_time.ToValue(); weekly_time.ToValue();
base::DictionaryValue expected_weekly_time; base::DictionaryValue expected_weekly_time;
expected_weekly_time.SetInteger("day_of_week", day_of_week()); expected_weekly_time.SetInteger(WeeklyTime::kDayOfWeek, day_of_week());
expected_weekly_time.SetInteger("time", minutes() * kMinute.InMilliseconds()); expected_weekly_time.SetInteger(WeeklyTime::kTime,
minutes() * kMinute.InMilliseconds());
if (timezone_offset()) { if (timezone_offset()) {
expected_weekly_time.SetInteger("timezone_offset", expected_weekly_time.SetInteger(WeeklyTime::kTimezoneOffset,
timezone_offset().value()); timezone_offset().value());
} }
EXPECT_EQ(*weekly_time_value, expected_weekly_time); EXPECT_EQ(*weekly_time_value, expected_weekly_time);
...@@ -117,6 +118,50 @@ TEST_P(SingleWeeklyTimeTest, ExtractFromProto_Valid) { ...@@ -117,6 +118,50 @@ TEST_P(SingleWeeklyTimeTest, ExtractFromProto_Valid) {
EXPECT_EQ(result->timezone_offset(), timezone_offset()); EXPECT_EQ(result->timezone_offset(), timezone_offset());
} }
TEST_P(SingleWeeklyTimeTest, ExtractFromValue_UnspecifiedDay) {
int milliseconds = minutes() * kMinute.InMilliseconds();
base::DictionaryValue value;
EXPECT_TRUE(value.SetIntKey(WeeklyTime::kDayOfWeek, kWeekdays[0]));
EXPECT_TRUE(value.SetIntKey(WeeklyTime::kTime, milliseconds));
auto result = WeeklyTime::ExtractFromValue(&value, timezone_offset());
ASSERT_FALSE(result);
}
TEST_P(SingleWeeklyTimeTest, ExtractFromValue_InvalidDay) {
int milliseconds = minutes() * kMinute.InMilliseconds();
base::DictionaryValue value;
EXPECT_TRUE(value.SetIntKey(WeeklyTime::kDayOfWeek, -1));
EXPECT_TRUE(value.SetIntKey(WeeklyTime::kTime, milliseconds));
auto result = WeeklyTime::ExtractFromValue(&value, timezone_offset());
ASSERT_FALSE(result);
EXPECT_TRUE(value.SetIntKey(WeeklyTime::kDayOfWeek, 8));
result = WeeklyTime::ExtractFromValue(&value, timezone_offset());
ASSERT_FALSE(result);
}
TEST_P(SingleWeeklyTimeTest, ExtractFromValue_InvalidTime) {
base::DictionaryValue value;
EXPECT_TRUE(
value.SetIntKey(WeeklyTime::kDayOfWeek, kWeekdays[day_of_week()]));
EXPECT_TRUE(value.SetIntKey(WeeklyTime::kTime, -1));
auto result = WeeklyTime::ExtractFromValue(&value, timezone_offset());
ASSERT_FALSE(result);
}
TEST_P(SingleWeeklyTimeTest, ExtractFromValue_Valid) {
int milliseconds = minutes() * kMinute.InMilliseconds();
base::DictionaryValue value;
EXPECT_TRUE(
value.SetIntKey(WeeklyTime::kDayOfWeek, kWeekdays[day_of_week()]));
EXPECT_TRUE(value.SetIntKey(WeeklyTime::kTime, milliseconds));
auto result = WeeklyTime::ExtractFromValue(&value, timezone_offset());
ASSERT_TRUE(result);
EXPECT_EQ(result->day_of_week(), day_of_week());
EXPECT_EQ(result->milliseconds(), milliseconds);
EXPECT_EQ(result->timezone_offset(), timezone_offset());
}
INSTANTIATE_TEST_SUITE_P( INSTANTIATE_TEST_SUITE_P(
TheSmallestCase, TheSmallestCase,
SingleWeeklyTimeTest, SingleWeeklyTimeTest,
......
...@@ -27,6 +27,8 @@ static_library("arc") { ...@@ -27,6 +27,8 @@ static_library("arc") {
"enterprise/arc_data_snapshotd_bridge.h", "enterprise/arc_data_snapshotd_bridge.h",
"enterprise/arc_data_snapshotd_manager.cc", "enterprise/arc_data_snapshotd_manager.cc",
"enterprise/arc_data_snapshotd_manager.h", "enterprise/arc_data_snapshotd_manager.h",
"enterprise/snapshot_hours_policy_service.cc",
"enterprise/snapshot_hours_policy_service.h",
"ime/arc_ime_bridge.h", "ime/arc_ime_bridge.h",
"ime/arc_ime_bridge_impl.cc", "ime/arc_ime_bridge_impl.cc",
"ime/arc_ime_bridge_impl.h", "ime/arc_ime_bridge_impl.h",
...@@ -103,6 +105,8 @@ static_library("arc") { ...@@ -103,6 +105,8 @@ static_library("arc") {
"//ash/keyboard/ui", "//ash/keyboard/ui",
"//ash/public/cpp", "//ash/public/cpp",
"//base", "//base",
"//base/util/timer",
"//chromeos",
"//chromeos/audio", "//chromeos/audio",
"//chromeos/constants", "//chromeos/constants",
"//chromeos/dbus", "//chromeos/dbus",
...@@ -386,6 +390,7 @@ source_set("unit_tests") { ...@@ -386,6 +390,7 @@ source_set("unit_tests") {
"clipboard/arc_clipboard_bridge_unittest.cc", "clipboard/arc_clipboard_bridge_unittest.cc",
"enterprise/arc_data_snapshotd_bridge_unittest.cc", "enterprise/arc_data_snapshotd_bridge_unittest.cc",
"enterprise/arc_data_snapshotd_manager_unittest.cc", "enterprise/arc_data_snapshotd_manager_unittest.cc",
"enterprise/snapshot_hours_policy_service_unittest.cc",
"ime/arc_ime_service_unittest.cc", "ime/arc_ime_service_unittest.cc",
"ime/key_event_result_receiver_unittest.cc", "ime/key_event_result_receiver_unittest.cc",
"intent_helper/activity_icon_loader_unittest.cc", "intent_helper/activity_icon_loader_unittest.cc",
...@@ -417,6 +422,8 @@ source_set("unit_tests") { ...@@ -417,6 +422,8 @@ source_set("unit_tests") {
"//ash/public/cpp", "//ash/public/cpp",
"//base", "//base",
"//base/test:test_support", "//base/test:test_support",
"//base/util/timer",
"//chromeos",
"//chromeos/constants", "//chromeos/constants",
"//chromeos/cryptohome:test_support", "//chromeos/cryptohome:test_support",
"//chromeos/dbus:test_support", "//chromeos/dbus:test_support",
......
...@@ -4,6 +4,7 @@ include_rules = [ ...@@ -4,6 +4,7 @@ include_rules = [
"+chromeos/cryptohome", "+chromeos/cryptohome",
"+chromeos/dbus", "+chromeos/dbus",
"+chromeos/memory", "+chromeos/memory",
"+chromeos/policy",
"+chromeos/system", "+chromeos/system",
"+components/guest_os", "+components/guest_os",
"+components/account_id", "+components/account_id",
......
// Copyright 2020 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 "components/arc/enterprise/snapshot_hours_policy_service.h"
#include <utility>
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/time/default_clock.h"
#include "base/time/tick_clock.h"
#include "base/values.h"
#include "chromeos/policy/weekly_time/time_utils.h"
#include "components/arc/arc_prefs.h"
#include "components/prefs/pref_service.h"
#include "components/user_manager/user_manager.h"
namespace arc {
namespace data_snapshotd {
SnapshotHoursPolicyService::SnapshotHoursPolicyService(PrefService* local_state)
: local_state_(local_state) {
DCHECK(local_state_);
pref_change_registrar_.Init(local_state_);
pref_change_registrar_.Add(
prefs::kArcSnapshotHours,
base::BindRepeating(&SnapshotHoursPolicyService::UpdatePolicy,
weak_ptr_factory_.GetWeakPtr()));
UpdatePolicy();
}
SnapshotHoursPolicyService::~SnapshotHoursPolicyService() = default;
void SnapshotHoursPolicyService::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
}
void SnapshotHoursPolicyService::RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer);
}
void SnapshotHoursPolicyService::StartObservingPrimaryProfilePrefs(
PrefService* profile_prefs) {
if (!user_manager::UserManager::Get() ||
!user_manager::UserManager::Get()->IsLoggedInAsPublicAccount()) {
// Do not care about ArcEnabled policy for other than MGS.
return;
}
profile_prefs_ = profile_prefs;
profile_pref_change_registrar_.Init(profile_prefs_);
profile_pref_change_registrar_.Add(
prefs::kArcEnabled,
base::BindRepeating(&SnapshotHoursPolicyService::UpdatePolicy,
weak_ptr_factory_.GetWeakPtr()));
UpdatePolicy();
}
void SnapshotHoursPolicyService::StopObservingPrimaryProfilePrefs() {
if (!profile_prefs_)
return;
profile_pref_change_registrar_.RemoveAll();
profile_prefs_ = nullptr;
UpdatePolicy();
}
void SnapshotHoursPolicyService::UpdatePolicy() {
intervals_.clear();
base::ScopedClosureRunner snapshot_disabler(
base::BindOnce(&SnapshotHoursPolicyService::DisableSnapshots,
weak_ptr_factory_.GetWeakPtr()));
if (!IsArcEnabled())
return;
const auto* dict = local_state_->GetDictionary(prefs::kArcSnapshotHours);
if (!dict)
return;
const auto* timezone = dict->FindStringKey("timezone");
if (!timezone)
return;
int offset;
if (!policy::weekly_time_utils::GetOffsetFromTimezoneToGmt(
*timezone, base::DefaultClock::GetInstance(), &offset)) {
return;
}
const auto* intervals = dict->FindListKey("intervals");
if (!intervals)
return;
for (const auto& entry : intervals->GetList()) {
if (!entry.is_dict())
continue;
auto interval =
policy::WeeklyTimeInterval::ExtractFromValue(&entry, -offset);
if (interval)
intervals_.push_back(*interval);
}
intervals_ = policy::weekly_time_utils::ConvertIntervalsToGmt(intervals_);
if (intervals_.empty())
return;
ignore_result(snapshot_disabler.Release());
EnableSnapshots();
}
void SnapshotHoursPolicyService::DisableSnapshots() {
if (!is_snapshot_enabled_)
return;
is_snapshot_enabled_ = false;
StopTimer();
SetEndTime(base::Time());
NotifySnapshotsDisabled();
}
void SnapshotHoursPolicyService::EnableSnapshots() {
if (is_snapshot_enabled_)
return;
is_snapshot_enabled_ = true;
UpdateTimer();
NotifySnapshotsEnabled();
}
void SnapshotHoursPolicyService::UpdateTimer() {
auto current_time = policy::WeeklyTime::GetCurrentGmtWeeklyTime(
base::DefaultClock::GetInstance());
for (const auto& interval : intervals_) {
if (interval.Contains(current_time)) {
auto remaining_timer_duration =
current_time.GetDurationTo(interval.end());
SetEndTime(base::Time::Now() + remaining_timer_duration);
StartTimer(remaining_timer_duration);
return;
}
}
StartTimer(policy::weekly_time_utils::GetDeltaTillNextTimeInterval(
current_time, intervals_));
SetEndTime(base::Time());
}
void SnapshotHoursPolicyService::StartTimer(base::TimeDelta delay) {
DCHECK_GT(delay, base::TimeDelta());
timer_.Start(FROM_HERE, base::DefaultClock::GetInstance()->Now() + delay,
base::BindOnce(&SnapshotHoursPolicyService::UpdateTimer,
weak_ptr_factory_.GetWeakPtr()));
}
void SnapshotHoursPolicyService::StopTimer() {
timer_.Stop();
}
void SnapshotHoursPolicyService::SetEndTime(base::Time end_time) {
if (snapshot_update_end_time_ == end_time)
return;
snapshot_update_end_time_ = end_time;
NotifySnapshotUpdateEndTimeChanged();
}
void SnapshotHoursPolicyService::NotifySnapshotsDisabled() {
for (auto& observer : observers_)
observer.OnSnapshotsDisabled();
}
void SnapshotHoursPolicyService::NotifySnapshotsEnabled() {
for (auto& observer : observers_)
observer.OnSnapshotsEnabled();
}
void SnapshotHoursPolicyService::NotifySnapshotUpdateEndTimeChanged() {
for (auto& observer : observers_)
observer.OnSnapshotUpdateEndTimeChanged();
}
bool SnapshotHoursPolicyService::IsArcEnabled() const {
// Assume ARC is enabled if there is no profile prefs.
return !profile_prefs_ || profile_prefs_->GetBoolean(prefs::kArcEnabled);
}
} // namespace data_snapshotd
} // namespace arc
// Copyright 2020 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 COMPONENTS_ARC_ENTERPRISE_SNAPSHOT_HOURS_POLICY_SERVICE_H_
#define COMPONENTS_ARC_ENTERPRISE_SNAPSHOT_HOURS_POLICY_SERVICE_H_
#include <vector>
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/observer_list_types.h"
#include "base/time/time.h"
#include "base/util/timer/wall_clock_timer.h"
#include "chromeos/policy/weekly_time/weekly_time_interval.h"
#include "components/prefs/pref_change_registrar.h"
class PrefService;
namespace arc {
namespace data_snapshotd {
// This class handles "DeviceArcDataSnapshotHours" policy, enables/disables ARC
// data snapshot feature, handles ARC data snapshot update intervals.
//
// ArcDataSnapshotdManager is an owner of this object.
class SnapshotHoursPolicyService {
public:
// Observer interface.
class Observer : public base::CheckedObserver {
public:
// Called once ARC data snapshot feature gets disabled by policy.
virtual void OnSnapshotsDisabled() {}
// Called once ARC data snapshot feature gets enabled by policy.
virtual void OnSnapshotsEnabled() {}
// Called once the interval for allowed ARC data snapshot update has
// started/finished.
// The observer can get an end time for started interval via
// snapshot_update_end_time().
virtual void OnSnapshotUpdateEndTimeChanged() {}
};
explicit SnapshotHoursPolicyService(PrefService* local_state);
SnapshotHoursPolicyService(const SnapshotHoursPolicyService&) = delete;
SnapshotHoursPolicyService& operator=(const SnapshotHoursPolicyService&) =
delete;
~SnapshotHoursPolicyService();
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
// Starts observing primary profile prefs only for MGS.
void StartObservingPrimaryProfilePrefs(PrefService* profile_prefs);
// Stops observing primary profile prefs.
void StopObservingPrimaryProfilePrefs();
// Returns the end time of the current interval when ARC data snapshot update
// is possible.
// Returns null outside of the interval.
base::Time snapshot_update_end_time() const {
return snapshot_update_end_time_;
}
bool is_snapshot_enabled() const { return is_snapshot_enabled_; }
const std::vector<policy::WeeklyTimeInterval>& get_intervals_for_testing()
const {
return intervals_;
}
const util::WallClockTimer* get_timer_for_testing() const { return &timer_; }
private:
// Processes the policy update: either ArcEnabled and
// DeviceArcDataSnapshotHours.
void UpdatePolicy();
// Disables ARC data snapshot feature and notifies observers if necessary.
void DisableSnapshots();
// Enables ARC data snaoshot feature and notifies observers if necessary.
void EnableSnapshots();
// Updates ARC data snapshot update timer according to the policy.
void UpdateTimer();
// Starts timer with |delay|.
void StartTimer(base::TimeDelta delay);
// Stops timer.
void StopTimer();
// Changes |snapshot_update_end_time_| and notifies observers if necessary.
void SetEndTime(base::Time end_time);
// Notifies observers about relevant events.
void NotifySnapshotsDisabled();
void NotifySnapshotsEnabled();
void NotifySnapshotUpdateEndTimeChanged();
// Returns false if ARC is disabled for a logged-in MGS, otherwise returns
// true.
bool IsArcEnabled() const;
// The feature is disabled when either kArcDataSnapshotHours policy is not set
// or ARC is disabled by policy for MGS.
bool is_snapshot_enabled_ = false;
// Not owned.
PrefService* const local_state_ = nullptr;
// Owned by primary profile.
PrefService* profile_prefs_ = nullptr;
// Registrar for pref changes in local_state_.
PrefChangeRegistrar pref_change_registrar_;
// Registrar for pref changes in profile_prefs_.
PrefChangeRegistrar profile_pref_change_registrar_;
// The end time of the current interval if ARC data snapshot update is
// possible. The value is null outside of all intervals.
base::Time snapshot_update_end_time_;
base::ObserverList<Observer> observers_;
// Current "ArcDataSnapshotHours" time intervals.
std::vector<policy::WeeklyTimeInterval> intervals_;
// Timer for updating ARC data snapshot at the begin of next interval or at
// the end of current interval.
util::WallClockTimer timer_;
base::WeakPtrFactory<SnapshotHoursPolicyService> weak_ptr_factory_{this};
};
} // namespace data_snapshotd
} // namespace arc
#endif // COMPONENTS_ARC_ENTERPRISE_SNAPSHOT_HOURS_POLICY_SERVICE_H_
// Copyright 2020 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 "components/arc/enterprise/snapshot_hours_policy_service.h"
#include <memory>
#include <string>
#include "base/bind_helpers.h"
#include "base/json/json_reader.h"
#include "base/optional.h"
#include "base/run_loop.h"
#include "base/test/task_environment.h"
#include "base/values.h"
#include "chromeos/policy/weekly_time/weekly_time_interval.h"
#include "components/arc/arc_prefs.h"
#include "components/prefs/testing_pref_service.h"
#include "components/user_manager/fake_user_manager.h"
#include "components/user_manager/scoped_user_manager.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace arc {
namespace data_snapshotd {
namespace {
constexpr char kPublicAccountEmail[] = "public-session-account@localhost";
// DeviceArcDataSnapshotHours policy with one correct interval.
constexpr char kJsonPolicy[] =
"{"
"\"intervals\": ["
"{"
"\"start\": {"
"\"day_of_week\": 1,"
"\"time\": 1284000"
"},"
"\"end\": {"
"\"day_of_week\": 1,"
"\"time\": 21720000"
"}"
"}"
"],"
"\"timezone\": \"GMT\""
"}";
// DeviceArcDataSnapshotHours incorrect policy with missing timezone.
constexpr char kJsonPolicyNoTimezone[] =
"{"
"\"intervals\": ["
"{"
"\"start\": {"
"\"day_of_week\": 1,"
"\"time\": 1284000"
"},"
"\"end\": {"
"\"day_of_week\": 1,"
"\"time\": 21720000"
"}"
"}"
"]"
"}";
// DeviceArcDataSnapshotHours incorrect policy with incorrect intervals.
constexpr char kJsonPolicyIncorrectIntervals[] =
"{"
"\"intervals\": ["
"{"
"\"start\": {"
"\"day_of_week\": 1,"
"\"time\": 1284000"
"},"
"\"end\": {"
"\"day_of_week\": 0,"
"\"time\": 21720000"
"}"
"}"
"],"
"\"timezone\": \"GMT\""
"}";
// DeviceArcDataSnapshotHours incorrect policy with missing intervals.
constexpr char kJsonPolicyNoIntervals[] =
"{"
"\"timezone\": \"GMT\""
"}";
// DeviceArcDataSnapshotHours incorrect policy with empty intervals.
constexpr char kJsonPolicyEmptyIntervals[] =
"{"
"\"intervals\": ["
"],"
"\"timezone\": \"GMT\""
"}";
// DeviceArcDataSnapshotHours incorrect policy with empty timezone.
constexpr char kJsonPolicyWrongOffset[] =
"{"
"\"intervals\": ["
"{"
"\"start\": {"
"\"day_of_week\": 1,"
"\"time\": 1284000"
"},"
"\"end\": {"
"\"day_of_week\": 1,"
"\"time\": 21720000"
"}"
"}"
"],"
"\"timezone\": \"\""
"}";
class FakeObserver : public SnapshotHoursPolicyService::Observer {
public:
FakeObserver() = default;
FakeObserver(const FakeObserver&) = delete;
FakeObserver& operator=(const FakeObserver&) = delete;
~FakeObserver() override = default;
void OnSnapshotsDisabled() override { disabled_calls_num_++; }
void OnSnapshotsEnabled() override { enabled_calls_num_++; }
void OnSnapshotUpdateEndTimeChanged() override { changed_calls_num_++; }
int disabled_calls_num() const { return disabled_calls_num_; }
int enabled_calls_num() const { return enabled_calls_num_; }
int changed_calls_num() const { return changed_calls_num_; }
private:
int disabled_calls_num_ = 0;
int enabled_calls_num_ = 0;
int changed_calls_num_ = 0;
};
} // namespace
// Tests SnapshotHoursPolicyService class instance.
class SnapshotHoursPolicyServiceTest
: public testing::TestWithParam<std::string> {
protected:
SnapshotHoursPolicyServiceTest() = default;
void SetUp() override {
arc::prefs::RegisterLocalStatePrefs(local_state_.registry());
policy_service_ =
std::make_unique<SnapshotHoursPolicyService>(local_state());
observer_ = std::make_unique<FakeObserver>();
policy_service()->AddObserver(observer_.get());
fake_user_manager_ = new user_manager::FakeUserManager();
scoped_user_manager_ = std::make_unique<user_manager::ScopedUserManager>(
base::WrapUnique(fake_user_manager_));
}
void TearDown() override {
policy_service()->RemoveObserver(observer_.get());
policy_service_.reset();
local_state_.ClearPref(prefs::kArcSnapshotHours);
}
const std::string& policy() const { return GetParam(); }
// Ensure the feature disabled.
void EnsureSnapshotDisabled(int disabled_calls_num = 0) {
EXPECT_FALSE(policy_service()->is_snapshot_enabled());
EXPECT_FALSE(policy_service()->get_timer_for_testing()->IsRunning());
EXPECT_TRUE(policy_service()->snapshot_update_end_time().is_null());
EXPECT_EQ(observer_->disabled_calls_num(), disabled_calls_num);
}
// Ensure the feature enabled.
void EnsureSnapshotEnabled(int enabled_calls_num = 1) {
EXPECT_TRUE(policy_service()->is_snapshot_enabled());
EXPECT_TRUE(policy_service()->get_timer_for_testing()->IsRunning());
EXPECT_EQ(policy_service()->get_intervals_for_testing().size(), 1u);
EXPECT_EQ(observer_->enabled_calls_num(), enabled_calls_num);
}
// Enable feature and check.
void EnableSnapshot(int enabled_calls_num = 1) {
base::Optional<base::Value> policy = base::JSONReader::Read(kJsonPolicy);
EXPECT_TRUE(policy.has_value());
local_state()->Set(arc::prefs::kArcSnapshotHours, policy.value());
EnsureSnapshotEnabled(enabled_calls_num);
}
// Fire the next timer.
void FastForwardToTimer() {
if (policy_service()->snapshot_update_end_time().is_null()) {
task_environment_.FastForwardBy(
policy_service()->get_timer_for_testing()->desired_run_time() -
base::Time::Now());
task_environment_.RunUntilIdle();
}
EXPECT_FALSE(policy_service()->snapshot_update_end_time().is_null());
}
void LoginAsPublicSession() {
auto account_id = AccountId::FromUserEmail(kPublicAccountEmail);
user_manager()->AddPublicAccountUser(account_id);
user_manager()->UserLoggedIn(account_id, account_id.GetUserEmail(), false,
false);
}
SnapshotHoursPolicyService* policy_service() { return policy_service_.get(); }
TestingPrefServiceSimple* local_state() { return &local_state_; }
FakeObserver* observer() { return observer_.get(); }
user_manager::FakeUserManager* user_manager() { return fake_user_manager_; }
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
private:
std::unique_ptr<FakeObserver> observer_;
std::unique_ptr<SnapshotHoursPolicyService> policy_service_;
TestingPrefServiceSimple local_state_;
user_manager::FakeUserManager* fake_user_manager_;
std::unique_ptr<user_manager::ScopedUserManager> scoped_user_manager_;
};
// Test that the feature is disabled by default.
TEST_F(SnapshotHoursPolicyServiceTest, Disabled) {
EnsureSnapshotDisabled();
}
TEST_F(SnapshotHoursPolicyServiceTest, OneIntervalEnabled) {
EnableSnapshot();
}
TEST_F(SnapshotHoursPolicyServiceTest, DoubleDisable) {
EnableSnapshot();
{
base::Optional<base::Value> policy_value =
base::JSONReader::Read(kJsonPolicyEmptyIntervals);
EXPECT_TRUE(policy_value.has_value());
local_state()->Set(arc::prefs::kArcSnapshotHours, policy_value.value());
EnsureSnapshotDisabled(1 /* disabled_calls_num */);
}
{
// User a different JSON to ensure the policy value is updated.
base::Optional<base::Value> policy_value =
base::JSONReader::Read(kJsonPolicyNoIntervals);
EXPECT_TRUE(policy_value.has_value());
local_state()->Set(arc::prefs::kArcSnapshotHours, policy_value.value());
// Do not notify the second time.
EnsureSnapshotDisabled(1 /* disabled_calls_num */);
}
}
TEST_F(SnapshotHoursPolicyServiceTest, DoubleEnable) {
EnableSnapshot();
// Do not notify the second time.
EnableSnapshot();
}
// Test that once the feature enabled, the time is outside interval.
TEST_F(SnapshotHoursPolicyServiceTest, OutsideInterval) {
EnableSnapshot();
EXPECT_TRUE(policy_service()->snapshot_update_end_time().is_null());
EXPECT_EQ(observer()->changed_calls_num(), 0);
FastForwardToTimer();
EXPECT_EQ(observer()->changed_calls_num(), 1);
EXPECT_FALSE(policy_service()->snapshot_update_end_time().is_null());
}
// Test that once the feature enabled, the time is outside interval.
TEST_F(SnapshotHoursPolicyServiceTest, InsideInterval) {
EnableSnapshot();
EXPECT_TRUE(policy_service()->snapshot_update_end_time().is_null());
EXPECT_EQ(observer()->changed_calls_num(), 0);
FastForwardToTimer();
EXPECT_EQ(observer()->changed_calls_num(), 1);
EXPECT_FALSE(policy_service()->snapshot_update_end_time().is_null());
// Disable snapshots.
{
base::Optional<base::Value> policy_value =
base::JSONReader::Read(kJsonPolicyNoIntervals);
EXPECT_TRUE(policy_value.has_value());
local_state()->Set(arc::prefs::kArcSnapshotHours, policy_value.value());
EnsureSnapshotDisabled(1 /* disabled_calls_num */);
EXPECT_EQ(observer()->changed_calls_num(), 2);
}
EnableSnapshot(2 /* enabled_calls_num */);
EXPECT_EQ(observer()->changed_calls_num(), 3);
EXPECT_FALSE(policy_service()->snapshot_update_end_time().is_null());
}
// Test that if ARC is disabled by user policy (not for public session
// account), it does not disable the feature.
TEST_F(SnapshotHoursPolicyServiceTest, DisableByUserPolicyForUser) {
EnableSnapshot();
TestingPrefServiceSimple profile_prefs;
arc::prefs::RegisterProfilePrefs(profile_prefs.registry());
profile_prefs.SetBoolean(arc::prefs::kArcEnabled, false);
policy_service()->StartObservingPrimaryProfilePrefs(&profile_prefs);
EnsureSnapshotEnabled();
policy_service()->StopObservingPrimaryProfilePrefs();
}
// Test that if ARC is disabled for public session account, it disables
// the feature.
TEST_F(SnapshotHoursPolicyServiceTest, DisableByUserPolicyForMGS) {
LoginAsPublicSession();
EnableSnapshot();
TestingPrefServiceSimple profile_prefs;
arc::prefs::RegisterProfilePrefs(profile_prefs.registry());
profile_prefs.SetBoolean(arc::prefs::kArcEnabled, false);
policy_service()->StartObservingPrimaryProfilePrefs(&profile_prefs);
EnsureSnapshotDisabled(1 /* disabled_calls_num */);
policy_service()->StopObservingPrimaryProfilePrefs();
EnsureSnapshotEnabled(2 /* enabled_calls_num */);
}
TEST_P(SnapshotHoursPolicyServiceTest, DisabledByPolicy) {
EnableSnapshot();
base::Optional<base::Value> policy_value = base::JSONReader::Read(policy());
EXPECT_TRUE(policy_value.has_value());
local_state()->Set(arc::prefs::kArcSnapshotHours, policy_value.value());
EnsureSnapshotDisabled(1 /* disabled_calls_num */);
}
INSTANTIATE_TEST_SUITE_P(
/* no prefix */,
SnapshotHoursPolicyServiceTest,
testing::Values(kJsonPolicyNoTimezone,
kJsonPolicyIncorrectIntervals,
kJsonPolicyNoIntervals,
kJsonPolicyEmptyIntervals,
kJsonPolicyWrongOffset));
} // namespace data_snapshotd
} // namespace arc
...@@ -259,7 +259,8 @@ bool FakeUserManager::IsLoggedInAsUserWithGaiaAccount() const { ...@@ -259,7 +259,8 @@ bool FakeUserManager::IsLoggedInAsUserWithGaiaAccount() const {
} }
bool FakeUserManager::IsLoggedInAsPublicAccount() const { bool FakeUserManager::IsLoggedInAsPublicAccount() const {
return false; const User* active_user = GetActiveUser();
return active_user && active_user->GetType() == USER_TYPE_PUBLIC_ACCOUNT;
} }
bool FakeUserManager::IsLoggedInAsGuest() const { bool FakeUserManager::IsLoggedInAsGuest() const {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment