• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright (C) 2017 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/next_update_check_policy_impl.h"
18 
19 #include <algorithm>
20 
21 #include "update_engine/common/utils.h"
22 
23 using base::Time;
24 using base::TimeDelta;
25 using std::max;
26 using std::string;
27 
28 namespace chromeos_update_manager {
29 
NextUpdateCheckTimePolicyImpl(const NextUpdateCheckPolicyConstants & constants)30 NextUpdateCheckTimePolicyImpl::NextUpdateCheckTimePolicyImpl(
31     const NextUpdateCheckPolicyConstants& constants)
32     : policy_constants_(constants) {}
33 
UpdateCheckAllowed(EvaluationContext * ec,State * state,string * error,UpdateCheckParams * result) const34 EvalStatus NextUpdateCheckTimePolicyImpl::UpdateCheckAllowed(
35     EvaluationContext* ec,
36     State* state,
37     string* error,
38     UpdateCheckParams* result) const {
39   // Ensure that periodic update checks are timed properly.
40   Time next_update_check;
41 
42   if (NextUpdateCheckTime(
43           ec, state, error, &next_update_check, policy_constants_) !=
44       EvalStatus::kSucceeded) {
45     return EvalStatus::kFailed;
46   }
47   if (!ec->IsWallclockTimeGreaterThan(next_update_check)) {
48     LOG(INFO) << "Periodic check interval not satisfied, blocking until "
49               << chromeos_update_engine::utils::ToString(next_update_check);
50     return EvalStatus::kAskMeAgainLater;
51   }
52 
53   return EvalStatus::kContinue;
54 }
55 
NextUpdateCheckTime(EvaluationContext * ec,State * state,string * error,Time * next_update_check,const NextUpdateCheckPolicyConstants & constants)56 EvalStatus NextUpdateCheckTimePolicyImpl::NextUpdateCheckTime(
57     EvaluationContext* ec,
58     State* state,
59     string* error,
60     Time* next_update_check,
61     const NextUpdateCheckPolicyConstants& constants) {
62   UpdaterProvider* const updater_provider = state->updater_provider();
63 
64   // Don't check for updates too often. We limit the update checks to once every
65   // some interval. The interval is kTimeoutInitialInterval the first time and
66   // kTimeoutPeriodicInterval for the subsequent update checks. If the update
67   // check fails, we increase the interval between the update checks
68   // exponentially until kTimeoutMaxBackoffInterval. Finally, to avoid having
69   // many chromebooks running update checks at the exact same time, we add some
70   // fuzz to the interval.
71   const Time* updater_started_time =
72       ec->GetValue(updater_provider->var_updater_started_time());
73   POLICY_CHECK_VALUE_AND_FAIL(updater_started_time, error);
74 
75   // This value is used for testing only and it will get deleted after the first
76   // time it is read.
77   const int64_t* interval_timeout =
78       ec->GetValue(updater_provider->var_test_update_check_interval_timeout());
79 
80   const Time* last_checked_time =
81       ec->GetValue(updater_provider->var_last_checked_time());
82 
83   const auto* seed = ec->GetValue(state->random_provider()->var_seed());
84   POLICY_CHECK_VALUE_AND_FAIL(seed, error);
85 
86   PRNG prng(*seed);
87 
88   // If this is the first attempt, compute and return an initial value.
89   if (last_checked_time == nullptr ||
90       *last_checked_time < *updater_started_time) {
91     TimeDelta time_diff =
92         interval_timeout == nullptr
93             ? FuzzedInterval(&prng,
94                              constants.timeout_initial_interval,
95                              constants.timeout_regular_fuzz)
96             : TimeDelta::FromSeconds(*interval_timeout);
97     *next_update_check = *updater_started_time + time_diff;
98     return EvalStatus::kSucceeded;
99   }
100 
101   if (interval_timeout != nullptr) {
102     *next_update_check =
103         *last_checked_time + TimeDelta::FromSeconds(*interval_timeout);
104     return EvalStatus::kSucceeded;
105   }
106   // Check whether the server is enforcing a poll interval; if not, this value
107   // will be zero.
108   const unsigned int* server_dictated_poll_interval =
109       ec->GetValue(updater_provider->var_server_dictated_poll_interval());
110   POLICY_CHECK_VALUE_AND_FAIL(server_dictated_poll_interval, error);
111 
112   int interval = *server_dictated_poll_interval;
113   int fuzz = 0;
114 
115   // If no poll interval was dictated by server compute a back-off period,
116   // starting from a predetermined base periodic interval and increasing
117   // exponentially by the number of consecutive failed attempts.
118   if (interval == 0) {
119     const unsigned int* consecutive_failed_update_checks =
120         ec->GetValue(updater_provider->var_consecutive_failed_update_checks());
121     POLICY_CHECK_VALUE_AND_FAIL(consecutive_failed_update_checks, error);
122 
123     interval = constants.timeout_periodic_interval;
124     unsigned int num_failures = *consecutive_failed_update_checks;
125     while (interval < constants.timeout_max_backoff_interval && num_failures) {
126       interval *= 2;
127       num_failures--;
128     }
129   }
130 
131   // We cannot back off longer than the predetermined maximum interval.
132   if (interval > constants.timeout_max_backoff_interval)
133     interval = constants.timeout_max_backoff_interval;
134 
135   // We cannot back off shorter than the predetermined periodic interval. Also,
136   // in this case set the fuzz to a predetermined regular value.
137   if (interval <= constants.timeout_periodic_interval) {
138     interval = constants.timeout_periodic_interval;
139     fuzz = constants.timeout_regular_fuzz;
140   }
141 
142   // If not otherwise determined, defer to a fuzz of +/-(interval / 2).
143   if (fuzz == 0)
144     fuzz = interval;
145 
146   *next_update_check =
147       *last_checked_time + FuzzedInterval(&prng, interval, fuzz);
148   return EvalStatus::kSucceeded;
149 }
150 
FuzzedInterval(PRNG * prng,int interval,int fuzz)151 TimeDelta NextUpdateCheckTimePolicyImpl::FuzzedInterval(PRNG* prng,
152                                                         int interval,
153                                                         int fuzz) {
154   DCHECK_GE(interval, 0);
155   DCHECK_GE(fuzz, 0);
156   int half_fuzz = fuzz / 2;
157   // This guarantees the output interval is non negative.
158   int interval_min = max(interval - half_fuzz, 0);
159   int interval_max = interval + half_fuzz;
160   return TimeDelta::FromSeconds(prng->RandMinMax(interval_min, interval_max));
161 }
162 
163 }  // namespace chromeos_update_manager
164