update_engine: Add Update Time Restrictions
Implementation for the update time restrictions policy where the admin
is allowed to choose time interval in which updates will not be checked.
- Adds WeeklyTime and WeeklyTimeInterval classes to update_engine to be
able to easily do interval range checking and time operations easily in
the policy. Added the wiring so the classes can be used as Values and
BoxedValues.
- Adds disallowed_intervals member to device_policy_provider, since this
contains the policy dictated disallowed intervals. The intervals are
obtained from libpolicy, a function was added to convert them to the
new WeeklyTime classes. Added the corresponding changes to the device
policy provider header and interface.
- Added the policy to chromeos_policy.cc so that it's taken into account
in UpdateCheckAllowed.
BUG=chromium:852860
TEST=cros_workon_make update_engine --test
Change-Id: If62a2b3650adf149ba87790345e1eb62f0e1bb1f
Reviewed-on: https://chromium-review.googlesource.com/1119397
Commit-Ready: Adolfo Higueros <adokar@google.com>
Tested-by: Adolfo Higueros <adokar@google.com>
Reviewed-by: Amin Hassani <ahassani@chromium.org>
diff --git a/Android.mk b/Android.mk
index 1beada9..acb98d0 100644
--- a/Android.mk
+++ b/Android.mk
@@ -336,6 +336,8 @@
update_manager/real_updater_provider.cc \
update_manager/state_factory.cc \
update_manager/update_manager.cc \
+ update_manager/update_time_restrictions_policy_impl.cc \
+ update_manager/weekly_time.cc \
update_status_utils.cc \
utils_android.cc
ifeq ($(local_use_binder),1)
@@ -1017,7 +1019,9 @@
update_manager/real_updater_provider_unittest.cc \
update_manager/umtest_utils.cc \
update_manager/update_manager_unittest.cc \
- update_manager/variable_unittest.cc
+ update_manager/update_time_restrictions_policy_impl_unittest.cc \
+ update_manager/variable_unittest.cc \
+ update_manager/weekly_time_unittest.cc
else # local_use_omaha == 1
LOCAL_STATIC_LIBRARIES += \
libupdate_engine_android \
diff --git a/update_engine.gyp b/update_engine.gyp
index 30daf2a..b09a401 100644
--- a/update_engine.gyp
+++ b/update_engine.gyp
@@ -294,6 +294,8 @@
'update_manager/real_updater_provider.cc',
'update_manager/state_factory.cc',
'update_manager/update_manager.cc',
+ 'update_manager/update_time_restrictions_policy_impl.cc',
+ 'update_manager/weekly_time.cc',
'update_status_utils.cc',
],
'conditions': [
@@ -579,7 +581,9 @@
'update_manager/real_updater_provider_unittest.cc',
'update_manager/umtest_utils.cc',
'update_manager/update_manager_unittest.cc',
+ 'update_manager/update_time_restrictions_policy_impl_unittest.cc',
'update_manager/variable_unittest.cc',
+ 'update_manager/weekly_time_unittest.cc',
],
},
],
diff --git a/update_manager/boxed_value.cc b/update_manager/boxed_value.cc
index b2505ee..971e9b7 100644
--- a/update_manager/boxed_value.cc
+++ b/update_manager/boxed_value.cc
@@ -29,6 +29,7 @@
#include "update_engine/update_manager/rollback_prefs.h"
#include "update_engine/update_manager/shill_provider.h"
#include "update_engine/update_manager/updater_provider.h"
+#include "update_engine/update_manager/weekly_time.h"
using chromeos_update_engine::ConnectionTethering;
using chromeos_update_engine::ConnectionType;
@@ -211,4 +212,23 @@
return retval;
}
+template <>
+string BoxedValue::ValuePrinter<WeeklyTimeInterval>(const void* value) {
+ const WeeklyTimeInterval* val =
+ reinterpret_cast<const WeeklyTimeInterval*>(value);
+ return val->ToString();
+}
+
+template <>
+string BoxedValue::ValuePrinter<WeeklyTimeIntervalVector>(const void* value) {
+ const WeeklyTimeIntervalVector* val =
+ reinterpret_cast<const WeeklyTimeIntervalVector*>(value);
+
+ string retval = "Disallowed intervals:\n";
+ for (const auto& interval : *val) {
+ retval += interval.ToString() + "\n";
+ }
+ return retval;
+}
+
} // namespace chromeos_update_manager
diff --git a/update_manager/boxed_value_unittest.cc b/update_manager/boxed_value_unittest.cc
index 04de3d4..3fa0f1a 100644
--- a/update_manager/boxed_value_unittest.cc
+++ b/update_manager/boxed_value_unittest.cc
@@ -30,6 +30,7 @@
#include "update_engine/update_manager/shill_provider.h"
#include "update_engine/update_manager/umtest_utils.h"
#include "update_engine/update_manager/updater_provider.h"
+#include "update_engine/update_manager/weekly_time.h"
using base::Time;
using base::TimeDelta;
@@ -258,4 +259,34 @@
.ToString());
}
+TEST(UmBoxedValueTest, WeeklyTimeIntervalToString) {
+ EXPECT_EQ("Start: day_of_week=2 time=100\nEnd: day_of_week=4 time=200",
+ BoxedValue(new WeeklyTimeInterval(
+ WeeklyTime(2, TimeDelta::FromMinutes(100)),
+ WeeklyTime(4, TimeDelta::FromMinutes(200))))
+ .ToString());
+ EXPECT_EQ("Start: day_of_week=1 time=10\nEnd: day_of_week=1 time=20",
+ BoxedValue(new WeeklyTimeInterval(
+ WeeklyTime(1, TimeDelta::FromMinutes(10)),
+ WeeklyTime(1, TimeDelta::FromMinutes(20))))
+ .ToString());
+}
+
+TEST(UmBoxedValueTest, WeeklyTimeIntervalVectorToString) {
+ WeeklyTimeIntervalVector intervals;
+ intervals.emplace_back(WeeklyTime(5, TimeDelta::FromMinutes(10)),
+ WeeklyTime(1, TimeDelta::FromMinutes(30)));
+ EXPECT_EQ(
+ "Disallowed intervals:\nStart: day_of_week=5 time=10\nEnd: "
+ "day_of_week=1 time=30\n",
+ BoxedValue(new WeeklyTimeIntervalVector(intervals)).ToString());
+ intervals.emplace_back(WeeklyTime(1, TimeDelta::FromMinutes(5)),
+ WeeklyTime(6, TimeDelta::FromMinutes(1000)));
+ EXPECT_EQ(
+ "Disallowed intervals:\nStart: day_of_week=5 time=10\nEnd: "
+ "day_of_week=1 time=30\nStart: day_of_week=1 time=5\nEnd: day_of_week=6 "
+ "time=1000\n",
+ BoxedValue(new WeeklyTimeIntervalVector(intervals)).ToString());
+}
+
} // namespace chromeos_update_manager
diff --git a/update_manager/chromeos_policy.cc b/update_manager/chromeos_policy.cc
index 95c47aa..f8ef84f 100644
--- a/update_manager/chromeos_policy.cc
+++ b/update_manager/chromeos_policy.cc
@@ -36,6 +36,7 @@
#include "update_engine/update_manager/out_of_box_experience_policy_impl.h"
#include "update_engine/update_manager/policy_utils.h"
#include "update_engine/update_manager/shill_provider.h"
+#include "update_engine/update_manager/update_time_restrictions_policy_impl.h"
using base::Time;
using base::TimeDelta;
@@ -213,6 +214,7 @@
OobePolicyImpl oobe_policy;
NextUpdateCheckTimePolicyImpl next_update_check_time_policy(
kNextUpdateCheckPolicyConstants);
+ UpdateTimeRestrictionsPolicyImpl update_time_restrictions_policy;
vector<Policy const*> policies_to_consult = {
// Do not perform any updates if there are not enough slots to do A/B
@@ -232,6 +234,9 @@
// If OOBE is enabled, wait until it is completed.
&oobe_policy,
+ // Ensure that updates are checked only in allowed times.
+ &update_time_restrictions_policy,
+
// Ensure that periodic update checks are timed properly.
&next_update_check_time_policy,
};
diff --git a/update_manager/chromeos_policy.h b/update_manager/chromeos_policy.h
index 67c0d15..d4ce4a6 100644
--- a/update_manager/chromeos_policy.h
+++ b/update_manager/chromeos_policy.h
@@ -114,6 +114,8 @@
UpdateCanStartAllowedP2PDownloadingBlockedDueToNumAttempts);
FRIEND_TEST(UmChromeOSPolicyTest,
UpdateCanStartAllowedP2PDownloadingBlockedDueToAttemptsPeriod);
+ FRIEND_TEST(UmChromeOSPolicyTest,
+ UpdateCheckAllowedNextUpdateCheckOutsideDisallowedInterval);
// Auxiliary constant (zero by default).
const base::TimeDelta kZeroInterval;
diff --git a/update_manager/chromeos_policy_unittest.cc b/update_manager/chromeos_policy_unittest.cc
index 524779f..184c241 100644
--- a/update_manager/chromeos_policy_unittest.cc
+++ b/update_manager/chromeos_policy_unittest.cc
@@ -21,6 +21,7 @@
#include "update_engine/update_manager/next_update_check_policy_impl.h"
#include "update_engine/update_manager/policy_test_utils.h"
+#include "update_engine/update_manager/weekly_time.h"
using base::Time;
using base::TimeDelta;
@@ -83,6 +84,9 @@
new bool(false));
fake_state_.device_policy_provider()->var_release_channel_delegated()->
reset(new bool(true));
+ fake_state_.device_policy_provider()
+ ->var_disallowed_time_intervals()
+ ->reset(new WeeklyTimeIntervalVector());
}
// Configures the policy to return a desired value from UpdateCheckAllowed by
@@ -142,6 +146,26 @@
EvalStatus::kSucceeded, &Policy::UpdateCheckAllowed, &result);
return result.rollback_allowed;
}
+
+ // Sets up a test with the given intervals and the current fake wallclock
+ // time.
+ void TestDisallowedTimeIntervals(const WeeklyTimeIntervalVector& intervals,
+ const EvalStatus& expected_status,
+ bool is_forced_update) {
+ SetUpDefaultTimeProvider();
+ SetUpdateCheckAllowed(true);
+
+ if (is_forced_update)
+ fake_state_.updater_provider()->var_forced_update_requested()->reset(
+ new UpdateRequestStatus(UpdateRequestStatus::kInteractive));
+ fake_state_.device_policy_provider()
+ ->var_disallowed_time_intervals()
+ ->reset(new WeeklyTimeIntervalVector(intervals));
+
+ // Check that |expected_status| matches the value of UpdateCheckAllowed
+ UpdateCheckParams result;
+ ExpectPolicyStatus(expected_status, &Policy::UpdateCheckAllowed, &result);
+ }
};
TEST_F(UmChromeOSPolicyTest, UpdateCheckAllowedWaitsForTheTimeout) {
@@ -304,6 +328,43 @@
}
TEST_F(UmChromeOSPolicyTest,
+ UpdateCheckAllowedWaitsForEndOfDisallowedInterval) {
+ // Check that the policy blocks during the disallowed checking intervals.
+ Time curr_time = fake_clock_.GetWallclockTime();
+ TestDisallowedTimeIntervals(
+ {WeeklyTimeInterval(
+ WeeklyTime::FromTime(curr_time),
+ WeeklyTime::FromTime(curr_time + TimeDelta::FromMinutes(1)))},
+ EvalStatus::kAskMeAgainLater,
+ false);
+}
+
+TEST_F(UmChromeOSPolicyTest,
+ UpdateCheckAllowedNoBlockOutsideDisallowedInterval) {
+ // Check that updates are allowed outside interval.
+ Time curr_time = fake_clock_.GetWallclockTime();
+ TestDisallowedTimeIntervals(
+ {WeeklyTimeInterval(
+ WeeklyTime::FromTime(curr_time - TimeDelta::FromMinutes(2)),
+ WeeklyTime::FromTime(curr_time - TimeDelta::FromMinutes(1)))},
+ EvalStatus::kSucceeded,
+ false);
+}
+
+TEST_F(UmChromeOSPolicyTest,
+ UpdateCheckAllowedDisallowedIntervalNoBlockWhenForced) {
+ // Check that updates are not blocked by this policy when an update is forced.
+ Time curr_time = fake_clock_.GetWallclockTime();
+ // Really big interval so that current time definitely falls in it.
+ TestDisallowedTimeIntervals(
+ {WeeklyTimeInterval(
+ WeeklyTime::FromTime(curr_time - TimeDelta::FromMinutes(1234)),
+ WeeklyTime::FromTime(curr_time + TimeDelta::FromMinutes(1234)))},
+ EvalStatus::kSucceeded,
+ true);
+}
+
+TEST_F(UmChromeOSPolicyTest,
UpdateCheckAllowedUpdatesDisabledWhenNotEnoughSlotsAbUpdates) {
// UpdateCheckAllowed should return false (kSucceeded) if the image booted
// without enough slots to do A/B updates.
diff --git a/update_manager/device_policy_provider.h b/update_manager/device_policy_provider.h
index 22613a1..c7e9d3a 100644
--- a/update_manager/device_policy_provider.h
+++ b/update_manager/device_policy_provider.h
@@ -27,6 +27,7 @@
#include "update_engine/update_manager/rollback_prefs.h"
#include "update_engine/update_manager/shill_provider.h"
#include "update_engine/update_manager/variable.h"
+#include "update_engine/update_manager/weekly_time.h"
namespace chromeos_update_manager {
@@ -75,6 +76,11 @@
virtual Variable<bool>* var_allow_kiosk_app_control_chrome_version() = 0;
+ // Variable that contains the time intervals during the week for which update
+ // checks are disallowed.
+ virtual Variable<WeeklyTimeIntervalVector>*
+ var_disallowed_time_intervals() = 0;
+
protected:
DevicePolicyProvider() {}
diff --git a/update_manager/fake_device_policy_provider.h b/update_manager/fake_device_policy_provider.h
index 957341c..70aca12 100644
--- a/update_manager/fake_device_policy_provider.h
+++ b/update_manager/fake_device_policy_provider.h
@@ -84,6 +84,11 @@
return &var_allow_kiosk_app_control_chrome_version_;
}
+ FakeVariable<WeeklyTimeIntervalVector>* var_disallowed_time_intervals()
+ override {
+ return &var_disallowed_time_intervals_;
+ }
+
private:
FakeVariable<bool> var_device_policy_is_loaded_{
"policy_is_loaded", kVariableModePoll};
@@ -110,6 +115,8 @@
FakeVariable<bool> var_au_p2p_enabled_{"au_p2p_enabled", kVariableModePoll};
FakeVariable<bool> var_allow_kiosk_app_control_chrome_version_{
"allow_kiosk_app_control_chrome_version", kVariableModePoll};
+ FakeVariable<WeeklyTimeIntervalVector> var_disallowed_time_intervals_{
+ "disallowed_time_intervals", kVariableModePoll};
DISALLOW_COPY_AND_ASSIGN(FakeDevicePolicyProvider);
};
diff --git a/update_manager/policy_test_utils.cc b/update_manager/policy_test_utils.cc
index d9a9857..5491e00 100644
--- a/update_manager/policy_test_utils.cc
+++ b/update_manager/policy_test_utils.cc
@@ -48,6 +48,17 @@
fake_clock_.SetWallclockTime(Time::FromInternalValue(12345678901234L));
}
+void UmPolicyTestBase::SetUpDefaultTimeProvider() {
+ Time current_time = fake_clock_.GetWallclockTime();
+ base::Time::Exploded exploded;
+ current_time.LocalExplode(&exploded);
+ fake_state_.time_provider()->var_curr_hour()->reset(new int(exploded.hour));
+ fake_state_.time_provider()->var_curr_minute()->reset(
+ new int(exploded.minute));
+ fake_state_.time_provider()->var_curr_date()->reset(
+ new Time(current_time.LocalMidnight()));
+}
+
void UmPolicyTestBase::SetUpDefaultState() {
fake_state_.updater_provider()->var_updater_started_time()->reset(
new Time(fake_clock_.GetWallclockTime()));
diff --git a/update_manager/policy_test_utils.h b/update_manager/policy_test_utils.h
index 5b93f7b..eb5758f 100644
--- a/update_manager/policy_test_utils.h
+++ b/update_manager/policy_test_utils.h
@@ -42,6 +42,9 @@
// Sets the clock to fixed values.
virtual void SetUpDefaultClock();
+ // Sets the fake time provider to the time given by the fake clock.
+ virtual void SetUpDefaultTimeProvider();
+
// Sets up the default state in fake_state_. override to add Policy-specific
// items, but only after calling this class's implementation.
virtual void SetUpDefaultState();
diff --git a/update_manager/real_device_policy_provider.cc b/update_manager/real_device_policy_provider.cc
index 3675624..b1135d9 100644
--- a/update_manager/real_device_policy_provider.cc
+++ b/update_manager/real_device_policy_provider.cc
@@ -18,6 +18,8 @@
#include <stdint.h>
+#include <vector>
+
#include <base/location.h>
#include <base/logging.h>
#include <base/time/time.h>
@@ -33,6 +35,7 @@
using policy::DevicePolicy;
using std::set;
using std::string;
+using std::vector;
namespace {
@@ -179,6 +182,23 @@
return true;
}
+bool RealDevicePolicyProvider::ConvertDisallowedTimeIntervals(
+ WeeklyTimeIntervalVector* disallowed_intervals_out) const {
+ vector<DevicePolicy::WeeklyTimeInterval> parsed_intervals;
+ if (!policy_provider_->GetDevicePolicy().GetDisallowedTimeIntervals(
+ &parsed_intervals)) {
+ return false;
+ }
+
+ disallowed_intervals_out->clear();
+ for (const auto& interval : parsed_intervals) {
+ disallowed_intervals_out->emplace_back(
+ WeeklyTime(interval.start_day_of_week, interval.start_time),
+ WeeklyTime(interval.end_day_of_week, interval.end_time));
+ }
+ return true;
+}
+
void RealDevicePolicyProvider::RefreshDevicePolicy() {
if (!policy_provider_->Reload()) {
LOG(INFO) << "No device policies/settings present.";
@@ -212,6 +232,8 @@
UpdateVariable(&var_au_p2p_enabled_, &DevicePolicy::GetAuP2PEnabled);
UpdateVariable(&var_allow_kiosk_app_control_chrome_version_,
&DevicePolicy::GetAllowKioskAppControlChromeVersion);
+ UpdateVariable(&var_disallowed_time_intervals_,
+ &RealDevicePolicyProvider::ConvertDisallowedTimeIntervals);
}
} // namespace chromeos_update_manager
diff --git a/update_manager/real_device_policy_provider.h b/update_manager/real_device_policy_provider.h
index 54216f1..88fbfa2 100644
--- a/update_manager/real_device_policy_provider.h
+++ b/update_manager/real_device_policy_provider.h
@@ -105,6 +105,10 @@
return &var_allow_kiosk_app_control_chrome_version_;
}
+ Variable<WeeklyTimeIntervalVector>* var_disallowed_time_intervals() override {
+ return &var_disallowed_time_intervals_;
+ }
+
private:
FRIEND_TEST(UmRealDevicePolicyProviderTest, RefreshScheduledTest);
FRIEND_TEST(UmRealDevicePolicyProviderTest, NonExistentDevicePolicyReloaded);
@@ -154,6 +158,12 @@
bool ConvertAllowedConnectionTypesForUpdate(
std::set<chromeos_update_engine::ConnectionType>* allowed_types) const;
+ // Wrapper for DevicePolicy::GetUpdateTimeRestrictions() that converts
+ // the DevicePolicy::WeeklyTimeInterval structs to WeeklyTimeInterval objects,
+ // which offer more functionality.
+ bool ConvertDisallowedTimeIntervals(
+ WeeklyTimeIntervalVector* disallowed_intervals_out) const;
+
// Used for fetching information about the device policy.
policy::PolicyProvider* policy_provider_;
@@ -191,6 +201,8 @@
AsyncCopyVariable<bool> var_au_p2p_enabled_{"au_p2p_enabled"};
AsyncCopyVariable<bool> var_allow_kiosk_app_control_chrome_version_{
"allow_kiosk_app_control_chrome_version"};
+ AsyncCopyVariable<WeeklyTimeIntervalVector> var_disallowed_time_intervals_{
+ "update_time_restrictions"};
DISALLOW_COPY_AND_ASSIGN(RealDevicePolicyProvider);
};
diff --git a/update_manager/real_device_policy_provider_unittest.cc b/update_manager/real_device_policy_provider_unittest.cc
index f5d0e23..49a5ec2 100644
--- a/update_manager/real_device_policy_provider_unittest.cc
+++ b/update_manager/real_device_policy_provider_unittest.cc
@@ -17,6 +17,7 @@
#include "update_engine/update_manager/real_device_policy_provider.h"
#include <memory>
+#include <vector>
#include <base/memory/ptr_util.h>
#include <brillo/message_loops/fake_message_loop.h>
@@ -40,12 +41,14 @@
using base::TimeDelta;
using brillo::MessageLoop;
using chromeos_update_engine::ConnectionType;
+using policy::DevicePolicy;
#if USE_DBUS
using chromeos_update_engine::dbus_test_utils::MockSignalHandler;
#endif // USE_DBUS
using std::set;
using std::string;
using std::unique_ptr;
+using std::vector;
using testing::DoAll;
using testing::Mock;
using testing::Return;
@@ -190,6 +193,7 @@
UmTestUtils::ExpectVariableNotSet(provider_->var_au_p2p_enabled());
UmTestUtils::ExpectVariableNotSet(
provider_->var_allow_kiosk_app_control_chrome_version());
+ UmTestUtils::ExpectVariableNotSet(provider_->var_disallowed_time_intervals());
}
TEST_F(UmRealDevicePolicyProviderTest, ValuesUpdated) {
@@ -327,4 +331,25 @@
provider_->var_allowed_connection_types_for_update());
}
+TEST_F(UmRealDevicePolicyProviderTest, DisallowedIntervalsConverted) {
+ SetUpExistentDevicePolicy();
+
+ vector<DevicePolicy::WeeklyTimeInterval> intervals = {
+ {5, TimeDelta::FromHours(5), 6, TimeDelta::FromHours(8)},
+ {1, TimeDelta::FromHours(1), 3, TimeDelta::FromHours(10)}};
+
+ EXPECT_CALL(mock_device_policy_, GetDisallowedTimeIntervals(_))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(intervals), Return(true)));
+ EXPECT_TRUE(provider_->Init());
+ loop_.RunOnce(false);
+
+ UmTestUtils::ExpectVariableHasValue(
+ WeeklyTimeIntervalVector{
+ WeeklyTimeInterval(WeeklyTime(5, TimeDelta::FromHours(5)),
+ WeeklyTime(6, TimeDelta::FromHours(8))),
+ WeeklyTimeInterval(WeeklyTime(1, TimeDelta::FromHours(1)),
+ WeeklyTime(3, TimeDelta::FromHours(10)))},
+ provider_->var_disallowed_time_intervals());
+}
+
} // namespace chromeos_update_manager
diff --git a/update_manager/update_time_restrictions_policy_impl.cc b/update_manager/update_time_restrictions_policy_impl.cc
new file mode 100644
index 0000000..baeb937
--- /dev/null
+++ b/update_manager/update_time_restrictions_policy_impl.cc
@@ -0,0 +1,65 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#include "update_engine/update_manager/update_time_restrictions_policy_impl.h"
+
+#include <memory>
+
+#include <base/time/time.h>
+
+#include "update_engine/update_manager/device_policy_provider.h"
+#include "update_engine/update_manager/system_provider.h"
+#include "update_engine/update_manager/weekly_time.h"
+
+using base::Time;
+using base::TimeDelta;
+
+namespace chromeos_update_manager {
+
+EvalStatus UpdateTimeRestrictionsPolicyImpl::UpdateCheckAllowed(
+ EvaluationContext* ec,
+ State* state,
+ std::string* error,
+ UpdateCheckParams* result) const {
+ TimeProvider* const time_provider = state->time_provider();
+ DevicePolicyProvider* const dp_provider = state->device_policy_provider();
+
+ const Time* curr_date = ec->GetValue(time_provider->var_curr_date());
+ const int* curr_hour = ec->GetValue(time_provider->var_curr_hour());
+ const int* curr_minute = ec->GetValue(time_provider->var_curr_minute());
+ if (!curr_date || !curr_hour || !curr_minute) {
+ LOG(WARNING) << "Unable to access local time.";
+ return EvalStatus::kContinue;
+ }
+
+ WeeklyTime now = WeeklyTime::FromTime(*curr_date);
+ now.AddTime(TimeDelta::FromHours(*curr_hour) +
+ TimeDelta::FromMinutes(*curr_minute));
+
+ const WeeklyTimeIntervalVector* intervals =
+ ec->GetValue(dp_provider->var_disallowed_time_intervals());
+ if (!intervals) {
+ return EvalStatus::kContinue;
+ }
+ for (const auto& interval : *intervals) {
+ if (interval.InRange(now)) {
+ return EvalStatus::kAskMeAgainLater;
+ }
+ }
+
+ return EvalStatus::kContinue;
+}
+
+} // namespace chromeos_update_manager
diff --git a/update_manager/update_time_restrictions_policy_impl.h b/update_manager/update_time_restrictions_policy_impl.h
new file mode 100644
index 0000000..1046531
--- /dev/null
+++ b/update_manager/update_time_restrictions_policy_impl.h
@@ -0,0 +1,57 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#ifndef UPDATE_ENGINE_UPDATE_MANAGER_UPDATE_TIME_RESTRICTIONS_POLICY_IMPL_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_UPDATE_TIME_RESTRICTIONS_POLICY_IMPL_H_
+
+#include <string>
+
+#include <base/time/time.h>
+
+#include "update_engine/update_manager/policy_utils.h"
+
+namespace chromeos_update_manager {
+
+// Policy that allows administrators to set time intervals during which
+// automatic update checks are disallowed. This implementation then checks if
+// the current time falls in the range spanned by the time intervals. If the
+// current time falls in one of the intervals then the update check is
+// blocked by this policy.
+class UpdateTimeRestrictionsPolicyImpl : public PolicyImplBase {
+ public:
+ UpdateTimeRestrictionsPolicyImpl() = default;
+ ~UpdateTimeRestrictionsPolicyImpl() override = default;
+
+ // When the current time is inside one of the intervals returns
+ // kAskMeAgainLater. If the current time is not inside any intervals returns
+ // kContinue. In case of errors, i.e. cannot access intervals or time, return
+ // kContinue.
+ EvalStatus UpdateCheckAllowed(EvaluationContext* ec,
+ State* state,
+ std::string* error,
+ UpdateCheckParams* result) const override;
+
+ protected:
+ std::string PolicyName() const override {
+ return "UpdateTimeRestrictionsPolicyImpl";
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(UpdateTimeRestrictionsPolicyImpl);
+};
+
+} // namespace chromeos_update_manager
+
+#endif // UPDATE_ENGINE_UPDATE_MANAGER_UPDATE_TIME_RESTRICTIONS_POLICY_IMPL_H_
diff --git a/update_manager/update_time_restrictions_policy_impl_unittest.cc b/update_manager/update_time_restrictions_policy_impl_unittest.cc
new file mode 100644
index 0000000..5c15821
--- /dev/null
+++ b/update_manager/update_time_restrictions_policy_impl_unittest.cc
@@ -0,0 +1,88 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/update_manager/update_time_restrictions_policy_impl.h"
+
+#include <memory>
+
+#include <base/time/time.h>
+
+#include "update_engine/update_manager/policy_test_utils.h"
+#include "update_engine/update_manager/weekly_time.h"
+
+using base::Time;
+using base::TimeDelta;
+using std::string;
+
+namespace chromeos_update_manager {
+
+constexpr TimeDelta kHour = TimeDelta::FromHours(1);
+constexpr TimeDelta kMinute = TimeDelta::FromMinutes(1);
+
+const WeeklyTimeIntervalVector kTestIntervals{
+ // Monday 10:15 AM to Monday 3:30 PM.
+ WeeklyTimeInterval(WeeklyTime(1, kHour * 10 + kMinute * 15),
+ WeeklyTime(1, kHour * 15 + kMinute * 30)),
+ // Wednesday 8:30 PM to Thursday 8:40 AM.
+ WeeklyTimeInterval(WeeklyTime(3, kHour * 20 + kMinute * 30),
+ WeeklyTime(4, kHour * 8 + kMinute * 40)),
+};
+
+class UmUpdateTimeRestrictionsPolicyImplTest : public UmPolicyTestBase {
+ protected:
+ UmUpdateTimeRestrictionsPolicyImplTest() {
+ policy_ = std::make_unique<UpdateTimeRestrictionsPolicyImpl>();
+ }
+
+ void TestPolicy(const Time::Exploded& exploded,
+ const WeeklyTimeIntervalVector& test_intervals,
+ const EvalStatus& expected_value) {
+ fake_clock_.SetWallclockTime(Time::FromLocalExploded(exploded));
+ SetUpDefaultTimeProvider();
+ fake_state_.device_policy_provider()
+ ->var_disallowed_time_intervals()
+ ->reset(new WeeklyTimeIntervalVector(test_intervals));
+ UpdateCheckParams result;
+ ExpectPolicyStatus(expected_value, &Policy::UpdateCheckAllowed, &result);
+ }
+};
+
+// If there are no intervals, then the check should always return kContinue.
+TEST_F(UmUpdateTimeRestrictionsPolicyImplTest, NoIntervalsSetTest) {
+ Time::Exploded random_time{2018, 7, 1, 9, 12, 30, 0, 0};
+ TestPolicy(random_time, WeeklyTimeIntervalVector(), EvalStatus::kContinue);
+}
+
+// Check that all intervals are checked.
+TEST_F(UmUpdateTimeRestrictionsPolicyImplTest, TimeInRange) {
+ // Monday, July 9th 2018 12:30 PM.
+ Time::Exploded first_interval_time{2018, 7, 1, 9, 12, 30, 0, 0};
+ TestPolicy(first_interval_time, kTestIntervals, EvalStatus::kAskMeAgainLater);
+
+ // Check second interval.
+ // Thursday, July 12th 2018 4:30 AM.
+ Time::Exploded second_interval_time{2018, 7, 4, 12, 4, 30, 0, 0};
+ TestPolicy(
+ second_interval_time, kTestIntervals, EvalStatus::kAskMeAgainLater);
+}
+
+TEST_F(UmUpdateTimeRestrictionsPolicyImplTest, TimeOutOfRange) {
+ // Monday, July 9th 2018 6:30 PM.
+ Time::Exploded out_of_range_time{2018, 7, 1, 9, 18, 30, 0, 0};
+ TestPolicy(out_of_range_time, kTestIntervals, EvalStatus::kContinue);
+}
+
+} // namespace chromeos_update_manager
diff --git a/update_manager/weekly_time.cc b/update_manager/weekly_time.cc
new file mode 100644
index 0000000..e478f9f
--- /dev/null
+++ b/update_manager/weekly_time.cc
@@ -0,0 +1,75 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#include "update_engine/update_manager/weekly_time.h"
+
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/stringprintf.h>
+#include <base/time/time.h>
+
+using base::Time;
+using base::TimeDelta;
+using std::string;
+
+namespace {
+const int kDaysInWeek = 7;
+}
+
+namespace chromeos_update_manager {
+
+TimeDelta WeeklyTime::GetDurationTo(const WeeklyTime& other) const {
+ if (other.TimeFromStartOfWeek() < TimeFromStartOfWeek()) {
+ return other.TimeFromStartOfWeek() +
+ (TimeDelta::FromDays(kDaysInWeek) - TimeFromStartOfWeek());
+ }
+ return other.TimeFromStartOfWeek() - TimeFromStartOfWeek();
+}
+
+TimeDelta WeeklyTime::TimeFromStartOfWeek() const {
+ return TimeDelta::FromDays(day_of_week_) + time_;
+}
+
+void WeeklyTime::AddTime(const TimeDelta& offset) {
+ time_ += offset;
+ int days_over = time_.InDays();
+ time_ -= TimeDelta::FromDays(days_over);
+ day_of_week_ = (day_of_week_ + days_over - 1) % kDaysInWeek + 1;
+}
+
+// static
+WeeklyTime WeeklyTime::FromTime(const Time& time) {
+ Time::Exploded exploded;
+ time.LocalExplode(&exploded);
+ return WeeklyTime(exploded.day_of_week,
+ TimeDelta::FromHours(exploded.hour) +
+ TimeDelta::FromMinutes(exploded.minute));
+}
+
+bool WeeklyTimeInterval::InRange(const WeeklyTime& time) const {
+ return time == start_ ||
+ (time.GetDurationTo(start_) >= time.GetDurationTo(end_) &&
+ time != end_);
+}
+
+string WeeklyTimeInterval::ToString() const {
+ return base::StringPrintf(
+ "Start: day_of_week=%d time=%d\nEnd: day_of_week=%d time=%d",
+ start_.day_of_week(),
+ start_.time().InMinutes(),
+ end_.day_of_week(),
+ end_.time().InMinutes());
+}
+
+} // namespace chromeos_update_manager
diff --git a/update_manager/weekly_time.h b/update_manager/weekly_time.h
new file mode 100644
index 0000000..9e3a039
--- /dev/null
+++ b/update_manager/weekly_time.h
@@ -0,0 +1,97 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#ifndef UPDATE_ENGINE_UPDATE_MANAGER_WEEKLY_TIME_H_
+#define UPDATE_ENGINE_UPDATE_MANAGER_WEEKLY_TIME_H_
+
+#include <string>
+#include <vector>
+
+#include <base/time/time.h>
+
+namespace chromeos_update_manager {
+
+// Represents a day of the week and the time since it started.
+class WeeklyTime {
+ public:
+ // Day of week (Sunday = 0 and so on) and time since start of the day (12 AM).
+ WeeklyTime(const int& day_of_week, const base::TimeDelta& time)
+ : day_of_week_(day_of_week), time_(time) {}
+
+ // Create a weekly time from a time object.
+ static WeeklyTime FromTime(const base::Time& time);
+
+ bool operator==(const WeeklyTime& other) const {
+ return time_ == other.time() && day_of_week_ == other.day_of_week();
+ }
+
+ bool operator!=(const WeeklyTime& other) const { return !(*this == other); }
+
+ // Return the duration between WeeklyTime and |other|. |other| is always
+ // considered to be after WeeklyTime. i.e. calling this function on [Friday
+ // 12:00, Monday 12:00] would return 3 days.
+ base::TimeDelta GetDurationTo(const WeeklyTime& other) const;
+
+ // Gets the weekly time represented as a time delta.
+ base::TimeDelta TimeFromStartOfWeek() const;
+
+ // Adds the given |offset| to the time with proper wraparound (e.g. Sunday + 1
+ // day = Monday).
+ void AddTime(const base::TimeDelta& offset);
+
+ int day_of_week() const { return day_of_week_; }
+
+ base::TimeDelta time() const { return time_; }
+
+ private:
+ int day_of_week_;
+ base::TimeDelta time_;
+};
+
+// Represents an interval of time during a week represented with WeeklyTime
+// objects. This interval can span at most 7 days. |end| is always considered to
+// be after |start|, this is possible since the times of the week are cyclic.
+// For example, the interval [Thursday 12:00, Monday 12:00) will span the time
+// between Thursday and Monday.
+class WeeklyTimeInterval {
+ public:
+ WeeklyTimeInterval(const WeeklyTime& start, const WeeklyTime& end)
+ : start_(start), end_(end) {}
+
+ // Determines if |time| is in this interval.
+ bool InRange(const WeeklyTime& time) const;
+
+ WeeklyTime start() const { return start_; }
+
+ WeeklyTime end() const { return end_; }
+
+ bool operator==(const WeeklyTimeInterval& other) const {
+ return start_ == other.start() && end_ == other.end();
+ }
+
+ // Converts the interval to a string. Used for the BoxedValue ToString
+ // function.
+ std::string ToString() const;
+
+ private:
+ WeeklyTime start_;
+ WeeklyTime end_;
+};
+
+using WeeklyTimeIntervalVector = std::vector<WeeklyTimeInterval>;
+
+} // namespace chromeos_update_manager
+
+#endif // UPDATE_ENGINE_UPDATE_MANAGER_WEEKLY_TIME_H_
diff --git a/update_manager/weekly_time_unittest.cc b/update_manager/weekly_time_unittest.cc
new file mode 100644
index 0000000..52c5425
--- /dev/null
+++ b/update_manager/weekly_time_unittest.cc
@@ -0,0 +1,212 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#include "update_engine/update_manager/weekly_time.h"
+
+#include <tuple>
+
+#include <base/time/time.h>
+#include <gtest/gtest.h>
+
+using base::TimeDelta;
+using std::tuple;
+
+namespace chromeos_update_manager {
+
+namespace {
+
+enum {
+ kSunday = 0,
+ kMonday,
+ kTuesday,
+ kWednesday,
+ kThursday,
+ kFriday,
+ kSaturday
+};
+
+} // namespace
+
+class WeeklyTimeDurationTest
+ : public testing::TestWithParam<tuple<int /* start_day_of_week */,
+ TimeDelta /* start_time */,
+ int /* end_day_of_week */,
+ TimeDelta /* end_time */,
+ TimeDelta /* expected result */>> {
+ protected:
+ int start_day_of_week() { return std::get<0>(GetParam()); }
+ TimeDelta start_time() { return std::get<1>(GetParam()); }
+ int end_day_of_week() { return std::get<2>(GetParam()); }
+ TimeDelta end_time() { return std::get<3>(GetParam()); }
+ TimeDelta result() { return std::get<4>(GetParam()); }
+};
+
+TEST_P(WeeklyTimeDurationTest, GetDurationTo) {
+ WeeklyTime start = WeeklyTime(start_day_of_week(), start_time());
+ WeeklyTime end = WeeklyTime(end_day_of_week(), end_time());
+
+ EXPECT_EQ(result(), start.GetDurationTo(end));
+}
+
+INSTANTIATE_TEST_CASE_P(
+ SameMinutes,
+ WeeklyTimeDurationTest,
+ testing::Values(std::make_tuple(kThursday,
+ TimeDelta::FromMinutes(30),
+ kSaturday,
+ TimeDelta::FromMinutes(30),
+ TimeDelta::FromDays(2))));
+
+INSTANTIATE_TEST_CASE_P(
+ DifferentMinutes,
+ WeeklyTimeDurationTest,
+ testing::Values(std::make_tuple(kMonday,
+ TimeDelta::FromMinutes(10),
+ kWednesday,
+ TimeDelta::FromMinutes(30),
+ TimeDelta::FromDays(2) +
+ TimeDelta::FromMinutes(20))));
+
+INSTANTIATE_TEST_CASE_P(
+ EndLessThanStartSameMinutes,
+ WeeklyTimeDurationTest,
+ testing::Values(std::make_tuple(kSaturday,
+ TimeDelta::FromMinutes(100),
+ kTuesday,
+ TimeDelta::FromMinutes(100),
+ TimeDelta::FromDays(3))));
+
+INSTANTIATE_TEST_CASE_P(
+ EndLessThanStartDifferentMinutes,
+ WeeklyTimeDurationTest,
+ testing::Values(std::make_tuple(kSaturday,
+ TimeDelta::FromMinutes(150),
+ kMonday,
+ TimeDelta::FromMinutes(10),
+ TimeDelta::FromDays(2) -
+ TimeDelta::FromMinutes(140))));
+
+class WeeklyTimeOffsetTest
+ : public testing::TestWithParam<tuple<int /* day_of_week */,
+ TimeDelta /* time */,
+ TimeDelta /* offset */,
+ WeeklyTime /* expected result */>> {
+ protected:
+ int day_of_week() { return std::get<0>(GetParam()); }
+ TimeDelta time() { return std::get<1>(GetParam()); }
+ TimeDelta offset() { return std::get<2>(GetParam()); }
+ WeeklyTime result() { return std::get<3>(GetParam()); }
+};
+
+TEST_P(WeeklyTimeOffsetTest, WeekTimeAddTime) {
+ WeeklyTime test_time = WeeklyTime(day_of_week(), time());
+ test_time.AddTime(offset());
+
+ EXPECT_EQ(result(), test_time);
+}
+
+INSTANTIATE_TEST_CASE_P(
+ SameDayTest,
+ WeeklyTimeOffsetTest,
+ testing::Values(std::make_tuple(kTuesday,
+ TimeDelta::FromMinutes(200),
+ TimeDelta::FromMinutes(400),
+ WeeklyTime(kTuesday,
+ TimeDelta::FromMinutes(600)))));
+
+INSTANTIATE_TEST_CASE_P(DayChangeTest,
+ WeeklyTimeOffsetTest,
+ testing::Values(std::make_tuple(
+ kThursday,
+ TimeDelta::FromHours(23),
+ TimeDelta::FromHours(2),
+ WeeklyTime(kFriday, TimeDelta::FromHours(1)))));
+
+INSTANTIATE_TEST_CASE_P(DayChangeTestOver7,
+ WeeklyTimeOffsetTest,
+ testing::Values(std::make_tuple(
+ kSunday,
+ TimeDelta::FromHours(20),
+ TimeDelta::FromDays(3),
+ WeeklyTime(kWednesday, TimeDelta::FromHours(20)))));
+
+class WeeklyTimeIntervalRangeTest
+ : public testing::TestWithParam<tuple<int /* test_day_of_week */,
+ int /* test_time */,
+ bool /* in regular interval */,
+ bool /* in short interval */,
+ bool /* |start| < | */>> {
+ protected:
+ int day_of_week() { return std::get<0>(GetParam()); }
+ int minutes() { return std::get<1>(GetParam()); }
+ bool regular_result() { return std::get<2>(GetParam()); }
+ bool short_result() { return std::get<3>(GetParam()); }
+ bool wraparound_result() { return std::get<4>(GetParam()); }
+};
+
+TEST_P(WeeklyTimeIntervalRangeTest, InRange) {
+ WeeklyTime test =
+ WeeklyTime(day_of_week(), TimeDelta::FromMinutes(minutes()));
+ WeeklyTimeInterval interval_regular =
+ WeeklyTimeInterval(WeeklyTime(kMonday, TimeDelta::FromMinutes(10)),
+ WeeklyTime(kWednesday, TimeDelta::FromMinutes(30)));
+ WeeklyTimeInterval interval_short =
+ WeeklyTimeInterval(WeeklyTime(kThursday, TimeDelta::FromMinutes(10)),
+ WeeklyTime(kThursday, TimeDelta::FromMinutes(11)));
+
+ WeeklyTimeInterval interval_wraparound =
+ WeeklyTimeInterval(WeeklyTime(kFriday, TimeDelta::FromMinutes(10)),
+ WeeklyTime(kTuesday, TimeDelta::FromMinutes(30)));
+
+ EXPECT_EQ(regular_result(), interval_regular.InRange(test));
+ EXPECT_EQ(short_result(), interval_short.InRange(test));
+ EXPECT_EQ(wraparound_result(), interval_wraparound.InRange(test));
+}
+
+// Test the left side of the range being inclusive.
+INSTANTIATE_TEST_CASE_P(
+ InclusiveSuccessLeft,
+ WeeklyTimeIntervalRangeTest,
+ testing::Values(std::make_tuple(kThursday, 10, false, true, false)));
+
+// Test the right side of the range being exclusive.
+INSTANTIATE_TEST_CASE_P(
+ ExclusiveSuccessRight,
+ WeeklyTimeIntervalRangeTest,
+ testing::Values(std::make_tuple(kThursday, 11, false, false, false)));
+
+// Test falling out of the interval by a small amount.
+INSTANTIATE_TEST_CASE_P(
+ FailOutsideRangeSmall,
+ WeeklyTimeIntervalRangeTest,
+ testing::Values(std::make_tuple(kThursday, 12, false, false, false)));
+
+// These test cases check that intervals wrap around properly.
+INSTANTIATE_TEST_CASE_P(
+ WraparoundOutside,
+ WeeklyTimeIntervalRangeTest,
+ testing::Values(std::make_tuple(kWednesday, 10, true, false, false)));
+
+INSTANTIATE_TEST_CASE_P(
+ WraparoundInsideRight,
+ WeeklyTimeIntervalRangeTest,
+ testing::Values(std::make_tuple(kSaturday, 10, false, false, true)));
+
+INSTANTIATE_TEST_CASE_P(
+ WraparoundInsideLeft,
+ WeeklyTimeIntervalRangeTest,
+ testing::Values(std::make_tuple(kMonday, 0, false, false, true)));
+
+} // namespace chromeos_update_manager