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