Commit 04902d11 authored by Pavol Marko's avatar Pavol Marko Committed by Commit Bot

[Reland] Add vector of intervals range checking to Time Restrictions policy

* Add function GetEndOfCurrent interval to obtain the last end of the
interval which contains the current time.
* Add function MaterializeIntervalsToLocalTimezone, which materializes
the given intervals to the current local timezone.
* Add function TimeIsInsideCrosSettingsIntervals, which checks if the
current time is inside the intervals set in cros settings. It then
returns the localized string of the end of the interval that contains
the current time.

This is a reland of CL:1132676 which fixes memory leaks that lead to a
revert (revert was CL:1172003).

BUG=chromium:852860
TEST=added unittests

Change-Id: I8ad01d4ab90d4e699e4b7377c24e4932513ebe1b
Reviewed-on: https://chromium-review.googlesource.com/1172128Reviewed-by: default avatarMaksim Ivanov <emaxx@chromium.org>
Commit-Queue: Pavol Marko <pmarko@chromium.org>
Cr-Commit-Position: refs/heads/master@{#582892}
parent 69c3eb58
......@@ -1385,6 +1385,8 @@ source_set("chromeos") {
"policy/configuration_policy_handler_chromeos.h",
"policy/device_auto_update_time_restrictions_decoder.cc",
"policy/device_auto_update_time_restrictions_decoder.h",
"policy/device_auto_update_time_restrictions_utils.cc",
"policy/device_auto_update_time_restrictions_utils.h",
"policy/device_cloud_policy_initializer.cc",
"policy/device_cloud_policy_initializer.h",
"policy/device_cloud_policy_manager_chromeos.cc",
......@@ -2144,6 +2146,7 @@ source_set("unit_tests") {
"policy/component_active_directory_policy_service_unittest.cc",
"policy/configuration_policy_handler_chromeos_unittest.cc",
"policy/device_auto_update_time_restrictions_decoder_unittest.cc",
"policy/device_auto_update_time_restrictions_utils_unittest.cc",
"policy/device_cloud_policy_initializer_unittest.cc",
"policy/device_cloud_policy_manager_chromeos_unittest.cc",
"policy/device_cloud_policy_store_chromeos_unittest.cc",
......
// Copyright 2018 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/chromeos/policy/device_auto_update_time_restrictions_utils.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/time/clock.h"
#include "base/values.h"
#include "chrome/browser/chromeos/policy/device_auto_update_time_restrictions_decoder.h"
#include "chrome/browser/chromeos/policy/weekly_time/time_utils.h"
#include "chrome/browser/chromeos/policy/weekly_time/weekly_time.h"
#include "chrome/browser/chromeos/policy/weekly_time/weekly_time_interval.h"
#include "chrome/browser/chromeos/settings/cros_settings.h"
#include "chromeos/settings/cros_settings_names.h"
#include "third_party/icu/source/i18n/unicode/timezone.h"
using base::Clock;
using base::ListValue;
using std::vector;
namespace policy {
namespace {
// Expects timezone agnostic |WeeklyTimeInterval|s in |intervals|.
// Transforms |intervals| into |*intervals_out|, converting them to the local
// timezone. The local timezone is determined using |clock|. If this returns
// false, an error occurred and the output vector should not be used. This can
// happen if the local timezone offset can not be determined, or if at least one
// of the passed intervals was already bound to a timezone.
bool MaterializeIntervalsToLocalTimezone(
const vector<WeeklyTimeInterval>& intervals,
Clock* clock,
vector<WeeklyTimeInterval>* intervals_out) {
DCHECK(intervals_out);
int local_to_gmt_offset;
auto local_time_zone = base::WrapUnique(icu::TimeZone::createDefault());
if (!weekly_time_utils::GetOffsetFromTimezoneToGmt(*local_time_zone, clock,
&local_to_gmt_offset)) {
LOG(ERROR) << "Unable to get local timezone.";
return false;
}
int gmt_to_local_offset = -local_to_gmt_offset;
intervals_out->clear();
for (const auto& interval : intervals) {
if (interval.start().timezone_offset()) {
LOG(ERROR) << "Intervals are not timezone-agnostic.";
return false;
}
intervals_out->push_back(WeeklyTimeInterval(
interval.start().ConvertToCustomTimezone(gmt_to_local_offset),
interval.end().ConvertToCustomTimezone(gmt_to_local_offset)));
}
return true;
}
} // namespace
bool GetDeviceAutoUpdateTimeRestrictionsIntervalsInLocalTimezone(
Clock* clock,
vector<WeeklyTimeInterval>* intervals_out) {
const ListValue* intervals_list;
if (!chromeos::CrosSettings::Get()->GetList(
chromeos::kDeviceAutoUpdateTimeRestrictions, &intervals_list)) {
return false;
}
vector<WeeklyTimeInterval> decoded_intervals;
if (!WeeklyTimeIntervalsFromListValue(*intervals_list, &decoded_intervals)) {
return false;
}
return MaterializeIntervalsToLocalTimezone(decoded_intervals, clock,
intervals_out);
}
} // namespace policy
// Copyright 2018 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_CHROMEOS_POLICY_DEVICE_AUTO_UPDATE_TIME_RESTRICTIONS_UTILS_H_
#define CHROME_BROWSER_CHROMEOS_POLICY_DEVICE_AUTO_UPDATE_TIME_RESTRICTIONS_UTILS_H_
#include <vector>
#include "base/optional.h"
namespace base {
class Clock;
}
namespace policy {
class WeeklyTimeInterval;
// Places the intervals for DeviceAutoUpdateTimeRestrictions according to
// CrosSettings into |intervals_out|. Note that the intervals are specified in
// a timezone-agnostic format by the policy, so this function converts them to
// the local timezone. It is thus not advisable to store the returned intervals
// for extended periods (because the local timezone could change). Returns false
// if there are no intervals set or if the conversion to the local timezone was
// unsuccessful.
bool GetDeviceAutoUpdateTimeRestrictionsIntervalsInLocalTimezone(
base::Clock* clock,
std::vector<WeeklyTimeInterval>* intervals_out);
} // namespace policy
#endif // CHROME_BROWSER_CHROMEOS_POLICY_DEVICE_AUTO_UPDATE_TIME_RESTRICTIONS_UTILS_H_
// Copyright 2018 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/chromeos/policy/device_auto_update_time_restrictions_utils.h"
#include <memory>
#include <string>
#include <tuple>
#include <utility>
#include "base/strings/string16.h"
#include "base/test/simple_test_clock.h"
#include "base/time/time.h"
#include "base/values.h"
#include "chrome/browser/chromeos/policy/weekly_time/weekly_time.h"
#include "chrome/browser/chromeos/policy/weekly_time/weekly_time_interval.h"
#include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h"
#include "chromeos/settings/cros_settings_names.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/icu/source/i18n/unicode/timezone.h"
using base::ListValue;
using base::Time;
using base::Value;
using std::tuple;
using std::vector;
namespace policy {
namespace {
enum {
kMonday = 1,
kTuesday = 2,
kWednesday = 3,
kThursday = 4,
kFriday = 5,
kSaturday = 6,
kSunday = 7,
};
std::string DayToString(int day_of_week) {
switch (day_of_week) {
case kMonday:
return "Monday";
case kTuesday:
return "Tuesday";
case kWednesday:
return "Wednesday";
case kThursday:
return "Thursday";
case kFriday:
return "Friday";
case kSaturday:
return "Saturday";
case kSunday:
return "Sunday";
}
return "";
}
const char* kNewYorkTimezone = "America/New_York";
constexpr int kMillisecondsInMinute = 60000;
constexpr int kMillisecondsInHour = 3600000;
constexpr int kNewYorkOffset = -4 * kMillisecondsInHour;
constexpr Time::Exploded kDaylightTime{2018, 8, 3, 8, 15, 0, 0, 0};
} // namespace
class DeviceAutoUpdateTimeRestrictionsUtilTest : public testing::Test {
protected:
void SetUp() override {
timezone_.reset(icu::TimeZone::createDefault());
icu::TimeZone::adoptDefault(
icu::TimeZone::createTimeZone(kNewYorkTimezone));
// Use Daylight savings to use EDT.
Time test_time;
ASSERT_TRUE(base::Time::FromUTCExploded(kDaylightTime, &test_time));
test_clock_.SetNow(test_time);
}
void TearDown() override { icu::TimeZone::adoptDefault(timezone_.release()); }
void SetCrosSettings(const std::string& path, const Value& in_value) {
cros_settings_helper_.Set(path, in_value);
cros_settings_helper_.ReplaceProvider(path);
}
ListValue GetIntervalsAsList(const vector<WeeklyTimeInterval>& intervals) {
ListValue list_val;
for (const auto& interval : intervals) {
base::DictionaryValue start;
int start_hours = interval.start().milliseconds() / kMillisecondsInHour;
int start_minutes = (interval.start().milliseconds() -
start_hours * kMillisecondsInHour) /
kMillisecondsInMinute;
start.SetKey("day_of_week",
Value(DayToString(interval.start().day_of_week())));
start.SetKey("hours", Value(start_hours));
start.SetKey("minutes", Value(start_minutes));
base::DictionaryValue end;
int end_hours = interval.end().milliseconds() / kMillisecondsInHour;
int end_minutes =
(interval.end().milliseconds() - end_hours * kMillisecondsInHour) /
kMillisecondsInMinute;
end.SetKey("day_of_week",
Value(DayToString(interval.end().day_of_week())));
end.SetKey("hours", Value(end_hours));
end.SetKey("minutes", Value(end_minutes));
base::DictionaryValue time_dict;
time_dict.SetKey("start", std::move(start));
time_dict.SetKey("end", std::move(end));
list_val.GetList().push_back(std::move(time_dict));
}
return list_val;
}
base::SimpleTestClock test_clock_;
private:
std::unique_ptr<icu::TimeZone> timezone_;
// These initialize CrosSettings and then tear down when the test is done.
chromeos::ScopedCrosSettingsTestHelper cros_settings_helper_;
};
TEST_F(DeviceAutoUpdateTimeRestrictionsUtilTest,
GetDeviceAutoUpdateTimeRestrictionsIntervalsInLocalTimezone) {
const vector<WeeklyTimeInterval> kExpected{
WeeklyTimeInterval(
WeeklyTime(kMonday, 5 * kMillisecondsInHour, kNewYorkOffset),
WeeklyTime(kTuesday, 10 * kMillisecondsInHour, kNewYorkOffset)),
WeeklyTimeInterval(
WeeklyTime(kWednesday, 10 * kMillisecondsInHour, kNewYorkOffset),
WeeklyTime(kWednesday, 15 * kMillisecondsInHour, kNewYorkOffset))};
SetCrosSettings(chromeos::kDeviceAutoUpdateTimeRestrictions,
GetIntervalsAsList(kExpected));
vector<WeeklyTimeInterval> result;
ASSERT_TRUE(GetDeviceAutoUpdateTimeRestrictionsIntervalsInLocalTimezone(
&test_clock_, &result));
EXPECT_EQ(result, kExpected);
}
} // namespace policy
......@@ -80,8 +80,8 @@ base::string16 WeeklyTimeToLocalizedString(const WeeklyTime& weekly_time,
if (!weekly_time.timezone_offset()) {
// Get offset to convert the WeeklyTime
int offset;
if (!GetOffsetFromTimezoneToGmt(*icu::TimeZone::createDefault(), clock,
&offset)) {
auto local_time_zone = base::WrapUnique(icu::TimeZone::createDefault());
if (!GetOffsetFromTimezoneToGmt(*local_time_zone, clock, &offset)) {
LOG(ERROR) << "Unable to obtain offset for time agnostic timezone";
return base::string16();
}
......@@ -120,5 +120,17 @@ base::TimeDelta GetDeltaTillNextTimeInterval(
return till_next_interval;
}
base::Optional<WeeklyTimeInterval> GetIntervalForCurrentTime(
const std::vector<WeeklyTimeInterval>& intervals,
base::Clock* clock) {
WeeklyTime weekly_time_now = WeeklyTime::GetCurrentGmtWeeklyTime(clock);
for (const auto& interval : intervals) {
if (interval.Contains(weekly_time_now)) {
return interval;
}
}
return base::nullopt;
}
} // namespace weekly_time_utils
} // namespace policy
......@@ -9,6 +9,7 @@
#include <string>
#include <vector>
#include "base/optional.h"
#include "base/strings/string16.h"
#include "third_party/icu/source/i18n/unicode/timezone.h"
......@@ -56,6 +57,14 @@ base::TimeDelta GetDeltaTillNextTimeInterval(
const WeeklyTime& current_time,
const std::vector<WeeklyTimeInterval>& weekly_time_intervals);
// Takes in a vector of weekly time intervals. If |clock->Now()|
// is inside one of the intervals, then the function returns the
// interval that contains |clock->Now()|. Otherwise, return |base::nullopt|.
// The intervals must have a defined
base::Optional<WeeklyTimeInterval> GetIntervalForCurrentTime(
const std::vector<WeeklyTimeInterval>& intervals,
base::Clock* clock);
} // namespace weekly_time_utils
} // namespace policy
......
......@@ -23,9 +23,25 @@ namespace policy {
namespace weekly_time_utils {
namespace {
enum {
kMonday = 1,
kTuesday = 2,
kWednesday = 3,
kThursday = 4,
kFriday = 5,
kSaturday = 6,
kSunday = 7,
};
constexpr int kMinutesInHour = 60;
constexpr int kMillisecondsInHour = 3600000;
constexpr base::TimeDelta kMinute = base::TimeDelta::FromMinutes(1);
constexpr base::TimeDelta kHour = base::TimeDelta::FromHours(1);
constexpr base::Time::Exploded kDaylightSavingsTime{2018, 8, 3, 8, 15, 0, 0, 0};
constexpr base::Time::Exploded kNonDaylightSavingsTime{2018, 1, 0, 28,
0, 0, 0, 0};
} // namespace
class TimeUtilsTimezoneFunctionsTest : public testing::Test {
......@@ -43,15 +59,15 @@ class TimeUtilsTimezoneFunctionsTest : public testing::Test {
void SetDaylightSavings(bool is_daylight_savings) {
if (is_daylight_savings) {
// Friday July 13th
base::Time::Exploded exploded_daylight{2018, 7, 5, 13, 0, 0, 0, 0};
base::Time test_time;
ASSERT_TRUE(base::Time::FromUTCExploded(exploded_daylight, &test_time));
ASSERT_TRUE(
base::Time::FromUTCExploded(kDaylightSavingsTime, &test_time));
test_clock_.SetNow(test_time);
} else {
// Sunday January 28
base::Time::Exploded exploded_standard{2018, 1, 0, 28, 0, 0, 0, 0};
base::Time test_time;
ASSERT_TRUE(base::Time::FromUTCExploded(exploded_standard, &test_time));
ASSERT_TRUE(
base::Time::FromUTCExploded(kNonDaylightSavingsTime, &test_time));
test_clock_.SetNow(test_time);
}
}
......@@ -144,5 +160,116 @@ TEST_F(TimeUtilsTimezoneFunctionsTest, GetOffsetFromTimezoneToGmtNoDaylight) {
EXPECT_EQ(result2, result);
}
class GetIntervalForCurrentTimeTest
: public testing::TestWithParam<
std::tuple<std::vector<WeeklyTimeInterval>,
base::Optional<WeeklyTimeInterval>>> {
protected:
void SetUp() override {
// Wednesday August 8th at 15:00 GMT
base::Time test_time;
ASSERT_TRUE(base::Time::FromUTCExploded(kDaylightSavingsTime, &test_time));
test_clock_.SetNow(test_time);
}
std::vector<WeeklyTimeInterval> intervals() {
return std::get<0>(GetParam());
}
base::Optional<WeeklyTimeInterval> expected_result() {
return std::get<1>(GetParam());
}
base::SimpleTestClock test_clock_;
};
TEST_P(GetIntervalForCurrentTimeTest, Test) {
base::Optional<WeeklyTimeInterval> result =
GetIntervalForCurrentTime(intervals(), &test_clock_);
EXPECT_EQ(result, expected_result());
}
INSTANTIATE_TEST_CASE_P(
SameTimezoneNone,
GetIntervalForCurrentTimeTest,
testing::Values(std::make_tuple(
std::vector<WeeklyTimeInterval>{
WeeklyTimeInterval(
WeeklyTime(kTuesday, 10 * kMillisecondsInHour, 0),
WeeklyTime(kWednesday, 8 * kMillisecondsInHour, 0)),
WeeklyTimeInterval(
WeeklyTime(kSunday, 5 * kMillisecondsInHour, 0),
WeeklyTime(kSunday, 16 * kMillisecondsInHour, 0))},
base::nullopt)));
INSTANTIATE_TEST_CASE_P(
SameTimezoneResult,
GetIntervalForCurrentTimeTest,
testing::Values(
std::make_tuple(
std::vector<WeeklyTimeInterval>{
WeeklyTimeInterval(
WeeklyTime(kTuesday, 10 * kMillisecondsInHour, 0),
WeeklyTime(kThursday, 8 * kMillisecondsInHour, 0)),
WeeklyTimeInterval(
WeeklyTime(kSunday, 5 * kMillisecondsInHour, 0),
WeeklyTime(kSunday, 16 * kMillisecondsInHour, 0))},
WeeklyTimeInterval(
WeeklyTime(kTuesday, 10 * kMillisecondsInHour, 0),
WeeklyTime(kThursday, 8 * kMillisecondsInHour, 0))),
std::make_tuple(
std::vector<WeeklyTimeInterval>{
WeeklyTimeInterval(
WeeklyTime(kTuesday, 10 * kMillisecondsInHour, 0),
WeeklyTime(kWednesday, 8 * kMillisecondsInHour, 0)),
WeeklyTimeInterval(
WeeklyTime(kSunday, 5 * kMillisecondsInHour, 0),
WeeklyTime(kWednesday, 16 * kMillisecondsInHour, 0))},
WeeklyTimeInterval(
WeeklyTime(kSunday, 5 * kMillisecondsInHour, 0),
WeeklyTime(kWednesday, 16 * kMillisecondsInHour, 0)))));
INSTANTIATE_TEST_CASE_P(
DifferentTimezoneNone,
GetIntervalForCurrentTimeTest,
testing::Values(std::make_tuple(
std::vector<WeeklyTimeInterval>{
WeeklyTimeInterval(WeeklyTime(kTuesday,
10 * kMillisecondsInHour,
5 * kMillisecondsInHour),
WeeklyTime(kWednesday,
17 * kMillisecondsInHour,
5 * kMillisecondsInHour)),
WeeklyTimeInterval(WeeklyTime(kSunday,
5 * kMillisecondsInHour,
5 * kMillisecondsInHour),
WeeklyTime(kSunday,
16 * kMillisecondsInHour,
5 * kMillisecondsInHour))},
base::nullopt)));
INSTANTIATE_TEST_CASE_P(
DifferentTimezoneResult,
GetIntervalForCurrentTimeTest,
testing::Values(std::make_tuple(
std::vector<WeeklyTimeInterval>{
WeeklyTimeInterval(WeeklyTime(kTuesday,
10 * kMillisecondsInHour,
-8 * kMillisecondsInHour),
WeeklyTime(kWednesday,
8 * kMillisecondsInHour,
-8 * kMillisecondsInHour)),
WeeklyTimeInterval(WeeklyTime(kSunday,
5 * kMillisecondsInHour,
-8 * kMillisecondsInHour),
WeeklyTime(kSunday,
16 * kMillisecondsInHour,
-8 * kMillisecondsInHour))},
WeeklyTimeInterval(WeeklyTime(kTuesday,
10 * kMillisecondsInHour,
-8 * kMillisecondsInHour),
WeeklyTime(kWednesday,
8 * kMillisecondsInHour,
-8 * kMillisecondsInHour)))));
} // namespace weekly_time_utils
} // namespace policy
......@@ -28,6 +28,12 @@ class WeeklyTime {
WeeklyTime& operator=(const WeeklyTime& rhs);
bool operator==(const WeeklyTime& rhs) const {
return day_of_week_ == rhs.day_of_week() &&
milliseconds_ == rhs.milliseconds() &&
timezone_offset_ == rhs.timezone_offset();
}
// Return DictionaryValue in format:
// { "day_of_week" : int # value is from 1 to 7 (1 = Monday, 2 = Tuesday,
// etc.)
......
......@@ -26,6 +26,10 @@ class WeeklyTimeInterval {
WeeklyTimeInterval& operator=(const WeeklyTimeInterval& rhs);
bool operator==(const WeeklyTimeInterval& rhs) const {
return start_ == rhs.start() && end_ == rhs.end();
}
// Return DictionaryValue in format:
// { "start" : WeeklyTime,
// "end" : WeeklyTime }
......
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