1 //
2 // Copyright (C) 2020 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 #include "update_engine/update_manager/update_time_restrictions_monitor.h"
18
19 #include <base/bind.h>
20 #include <base/time/time.h>
21
22 #include "update_engine/common/system_state.h"
23
24 using base::TimeDelta;
25 using brillo::MessageLoop;
26 using chromeos_update_engine::SystemState;
27
28 namespace chromeos_update_manager {
29
30 namespace {
31
FindNextNearestInterval(const WeeklyTimeIntervalVector & intervals,const WeeklyTime & now)32 const WeeklyTimeInterval* FindNextNearestInterval(
33 const WeeklyTimeIntervalVector& intervals, const WeeklyTime& now) {
34 const WeeklyTimeInterval* result_interval = nullptr;
35 // As we are dealing with weekly time here, the maximum duration can be one
36 // week.
37 TimeDelta duration_till_next_interval = TimeDelta::FromDays(7);
38 for (const auto& interval : intervals) {
39 if (interval.InRange(now)) {
40 return &interval;
41 }
42 const TimeDelta current_duration = now.GetDurationTo(interval.start());
43 if (current_duration < duration_till_next_interval) {
44 result_interval = &interval;
45 duration_till_next_interval = current_duration;
46 }
47 }
48 return result_interval;
49 }
50
Now()51 WeeklyTime Now() {
52 return WeeklyTime::FromTime(SystemState::Get()->clock()->GetWallclockTime());
53 }
54
55 } // namespace
56
UpdateTimeRestrictionsMonitor(DevicePolicyProvider * device_policy_provider,Delegate * delegate)57 UpdateTimeRestrictionsMonitor::UpdateTimeRestrictionsMonitor(
58 DevicePolicyProvider* device_policy_provider, Delegate* delegate)
59 : evaluation_context_(/* evaluation_timeout = */ TimeDelta::Max(),
60 /* expiration_timeout = */ TimeDelta::Max(),
61 /* unregister_cb = */ {}),
62 device_policy_provider_(device_policy_provider),
63 delegate_(delegate),
64 weak_ptr_factory_(this) {
65 if (device_policy_provider_ != nullptr && delegate_ != nullptr)
66 StartMonitoring();
67 }
68
~UpdateTimeRestrictionsMonitor()69 UpdateTimeRestrictionsMonitor::~UpdateTimeRestrictionsMonitor() {
70 StopMonitoring();
71 }
72
StartMonitoring()73 void UpdateTimeRestrictionsMonitor::StartMonitoring() {
74 DCHECK(device_policy_provider_);
75 const WeeklyTimeIntervalVector* new_intervals = evaluation_context_.GetValue(
76 device_policy_provider_->var_disallowed_time_intervals());
77 if (new_intervals && !new_intervals->empty())
78 WaitForRestrictedIntervalStarts(*new_intervals);
79
80 const bool is_registered = evaluation_context_.RunOnValueChangeOrTimeout(
81 base::Bind(&UpdateTimeRestrictionsMonitor::OnIntervalsChanged,
82 base::Unretained(this)));
83 DCHECK(is_registered);
84 }
85
WaitForRestrictedIntervalStarts(const WeeklyTimeIntervalVector & restricted_time_intervals)86 void UpdateTimeRestrictionsMonitor::WaitForRestrictedIntervalStarts(
87 const WeeklyTimeIntervalVector& restricted_time_intervals) {
88 DCHECK(!restricted_time_intervals.empty());
89
90 const WeeklyTimeInterval* current_interval =
91 FindNextNearestInterval(restricted_time_intervals, Now());
92 if (current_interval == nullptr) {
93 LOG(WARNING) << "Could not find next nearest restricted interval.";
94 return;
95 }
96
97 // If |current_interval| happens right now, set delay to zero.
98 const TimeDelta duration_till_start =
99 current_interval->InRange(Now())
100 ? TimeDelta::FromMicroseconds(0)
101 : Now().GetDurationTo(current_interval->start());
102 LOG(INFO) << "Found restricted interval starting at "
103 << (SystemState::Get()->clock()->GetWallclockTime() +
104 duration_till_start);
105
106 timeout_event_ = MessageLoop::current()->PostDelayedTask(
107 FROM_HERE,
108 base::Bind(&UpdateTimeRestrictionsMonitor::HandleRestrictedIntervalStarts,
109 weak_ptr_factory_.GetWeakPtr()),
110 duration_till_start);
111 }
112
HandleRestrictedIntervalStarts()113 void UpdateTimeRestrictionsMonitor::HandleRestrictedIntervalStarts() {
114 timeout_event_ = MessageLoop::kTaskIdNull;
115 if (delegate_)
116 delegate_->OnRestrictedIntervalStarts();
117 }
118
StopMonitoring()119 void UpdateTimeRestrictionsMonitor::StopMonitoring() {
120 MessageLoop::current()->CancelTask(timeout_event_);
121 timeout_event_ = MessageLoop::kTaskIdNull;
122 }
123
OnIntervalsChanged()124 void UpdateTimeRestrictionsMonitor::OnIntervalsChanged() {
125 DCHECK(!evaluation_context_.is_expired());
126
127 StopMonitoring();
128 evaluation_context_.ResetEvaluation();
129 StartMonitoring();
130 }
131
132 } // namespace chromeos_update_manager
133