blob: 6f9748e33b586676c6985d7d3b293e80e30ed6cc [file] [log] [blame]
Aaron Wood56d8ab32017-09-22 15:56:18 -07001//
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
23using base::Time;
24using base::TimeDelta;
25using std::max;
26using std::string;
27
28namespace chromeos_update_manager {
29
30NextUpdateCheckTimePolicyImpl::NextUpdateCheckTimePolicyImpl(
31 const NextUpdateCheckPolicyConstants& constants)
32 : policy_constants_(constants) {}
33
34EvalStatus 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
56EvalStatus 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 const Time* last_checked_time =
76 ec->GetValue(updater_provider->var_last_checked_time());
77
78 const auto* seed = ec->GetValue(state->random_provider()->var_seed());
79 POLICY_CHECK_VALUE_AND_FAIL(seed, error);
80
81 PRNG prng(*seed);
82
83 // If this is the first attempt, compute and return an initial value.
84 if (last_checked_time == nullptr ||
85 *last_checked_time < *updater_started_time) {
86 *next_update_check = *updater_started_time +
87 FuzzedInterval(&prng,
88 constants.timeout_initial_interval,
89 constants.timeout_regular_fuzz);
90 return EvalStatus::kSucceeded;
91 }
92
93 // Check whether the server is enforcing a poll interval; if not, this value
94 // will be zero.
95 const unsigned int* server_dictated_poll_interval =
96 ec->GetValue(updater_provider->var_server_dictated_poll_interval());
97 POLICY_CHECK_VALUE_AND_FAIL(server_dictated_poll_interval, error);
98
99 int interval = *server_dictated_poll_interval;
100 int fuzz = 0;
101
102 // If no poll interval was dictated by server compute a back-off period,
103 // starting from a predetermined base periodic interval and increasing
104 // exponentially by the number of consecutive failed attempts.
105 if (interval == 0) {
106 const unsigned int* consecutive_failed_update_checks =
107 ec->GetValue(updater_provider->var_consecutive_failed_update_checks());
108 POLICY_CHECK_VALUE_AND_FAIL(consecutive_failed_update_checks, error);
109
110 interval = constants.timeout_periodic_interval;
111 unsigned int num_failures = *consecutive_failed_update_checks;
112 while (interval < constants.timeout_max_backoff_interval && num_failures) {
113 interval *= 2;
114 num_failures--;
115 }
116 }
117
118 // We cannot back off longer than the predetermined maximum interval.
119 if (interval > constants.timeout_max_backoff_interval)
120 interval = constants.timeout_max_backoff_interval;
121
122 // We cannot back off shorter than the predetermined periodic interval. Also,
123 // in this case set the fuzz to a predetermined regular value.
124 if (interval <= constants.timeout_periodic_interval) {
125 interval = constants.timeout_periodic_interval;
126 fuzz = constants.timeout_regular_fuzz;
127 }
128
129 // If not otherwise determined, defer to a fuzz of +/-(interval / 2).
130 if (fuzz == 0)
131 fuzz = interval;
132
133 *next_update_check =
134 *last_checked_time + FuzzedInterval(&prng, interval, fuzz);
135 return EvalStatus::kSucceeded;
136}
137
138TimeDelta NextUpdateCheckTimePolicyImpl::FuzzedInterval(PRNG* prng,
139 int interval,
140 int fuzz) {
141 DCHECK_GE(interval, 0);
142 DCHECK_GE(fuzz, 0);
143 int half_fuzz = fuzz / 2;
144 // This guarantees the output interval is non negative.
145 int interval_min = max(interval - half_fuzz, 0);
146 int interval_max = interval + half_fuzz;
147 return TimeDelta::FromSeconds(prng->RandMinMax(interval_min, interval_max));
148}
149
150} // namespace chromeos_update_manager