1cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood//
2cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood// Copyright (C) 2017 The Android Open Source Project
3cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood//
4cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood// Licensed under the Apache License, Version 2.0 (the "License");
5cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood// you may not use this file except in compliance with the License.
6cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood// You may obtain a copy of the License at
7cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood//
8cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood//      http://www.apache.org/licenses/LICENSE-2.0
9cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood//
10cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood// Unless required by applicable law or agreed to in writing, software
11cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood// distributed under the License is distributed on an "AS IS" BASIS,
12cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood// See the License for the specific language governing permissions and
14cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood// limitations under the License.
15cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood//
16cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood
17cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood#include "update_engine/update_manager/next_update_check_policy_impl.h"
18cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood
19cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood#include <algorithm>
20cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood
21cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood#include "update_engine/common/utils.h"
22cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood
23cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Woodusing base::Time;
24cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Woodusing base::TimeDelta;
25cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Woodusing std::max;
26cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Woodusing std::string;
27cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood
28cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Woodnamespace chromeos_update_manager {
29cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood
30cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron WoodNextUpdateCheckTimePolicyImpl::NextUpdateCheckTimePolicyImpl(
31cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood    const NextUpdateCheckPolicyConstants& constants)
32cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood    : policy_constants_(constants) {}
33cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood
34cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron WoodEvalStatus NextUpdateCheckTimePolicyImpl::UpdateCheckAllowed(
35cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood    EvaluationContext* ec,
36cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood    State* state,
37cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood    string* error,
38cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood    UpdateCheckParams* result) const {
39cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  // Ensure that periodic update checks are timed properly.
40cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  Time next_update_check;
41cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood
42cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  if (NextUpdateCheckTime(
43cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood          ec, state, error, &next_update_check, policy_constants_) !=
44cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood      EvalStatus::kSucceeded) {
45cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood    return EvalStatus::kFailed;
46cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  }
47cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  if (!ec->IsWallclockTimeGreaterThan(next_update_check)) {
48cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood    LOG(INFO) << "Periodic check interval not satisfied, blocking until "
49cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood              << chromeos_update_engine::utils::ToString(next_update_check);
50cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood    return EvalStatus::kAskMeAgainLater;
51cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  }
52cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood
53cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  return EvalStatus::kContinue;
54cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood}
55cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood
56cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron WoodEvalStatus NextUpdateCheckTimePolicyImpl::NextUpdateCheckTime(
57cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood    EvaluationContext* ec,
58cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood    State* state,
59cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood    string* error,
60cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood    Time* next_update_check,
61cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood    const NextUpdateCheckPolicyConstants& constants) {
62cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  UpdaterProvider* const updater_provider = state->updater_provider();
63cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood
64cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  // Don't check for updates too often. We limit the update checks to once every
65cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  // some interval. The interval is kTimeoutInitialInterval the first time and
66cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  // kTimeoutPeriodicInterval for the subsequent update checks. If the update
67cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  // check fails, we increase the interval between the update checks
68cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  // exponentially until kTimeoutMaxBackoffInterval. Finally, to avoid having
69cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  // many chromebooks running update checks at the exact same time, we add some
70cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  // fuzz to the interval.
71cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  const Time* updater_started_time =
72cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood      ec->GetValue(updater_provider->var_updater_started_time());
73cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  POLICY_CHECK_VALUE_AND_FAIL(updater_started_time, error);
74cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood
75cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  const Time* last_checked_time =
76cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood      ec->GetValue(updater_provider->var_last_checked_time());
77cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood
78cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  const auto* seed = ec->GetValue(state->random_provider()->var_seed());
79cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  POLICY_CHECK_VALUE_AND_FAIL(seed, error);
80cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood
81cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  PRNG prng(*seed);
82cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood
83cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  // If this is the first attempt, compute and return an initial value.
84cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  if (last_checked_time == nullptr ||
85cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood      *last_checked_time < *updater_started_time) {
86cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood    *next_update_check = *updater_started_time +
87cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood                         FuzzedInterval(&prng,
88cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood                                        constants.timeout_initial_interval,
89cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood                                        constants.timeout_regular_fuzz);
90cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood    return EvalStatus::kSucceeded;
91cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  }
92cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood
93cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  // Check whether the server is enforcing a poll interval; if not, this value
94cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  // will be zero.
95cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  const unsigned int* server_dictated_poll_interval =
96cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood      ec->GetValue(updater_provider->var_server_dictated_poll_interval());
97cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  POLICY_CHECK_VALUE_AND_FAIL(server_dictated_poll_interval, error);
98cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood
99cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  int interval = *server_dictated_poll_interval;
100cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  int fuzz = 0;
101cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood
102cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  // If no poll interval was dictated by server compute a back-off period,
103cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  // starting from a predetermined base periodic interval and increasing
104cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  // exponentially by the number of consecutive failed attempts.
105cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  if (interval == 0) {
106cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood    const unsigned int* consecutive_failed_update_checks =
107cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood        ec->GetValue(updater_provider->var_consecutive_failed_update_checks());
108cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood    POLICY_CHECK_VALUE_AND_FAIL(consecutive_failed_update_checks, error);
109cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood
110cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood    interval = constants.timeout_periodic_interval;
111cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood    unsigned int num_failures = *consecutive_failed_update_checks;
112cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood    while (interval < constants.timeout_max_backoff_interval && num_failures) {
113cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood      interval *= 2;
114cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood      num_failures--;
115cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood    }
116cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  }
117cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood
118cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  // We cannot back off longer than the predetermined maximum interval.
119cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  if (interval > constants.timeout_max_backoff_interval)
120cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood    interval = constants.timeout_max_backoff_interval;
121cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood
122cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  // We cannot back off shorter than the predetermined periodic interval. Also,
123cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  // in this case set the fuzz to a predetermined regular value.
124cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  if (interval <= constants.timeout_periodic_interval) {
125cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood    interval = constants.timeout_periodic_interval;
126cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood    fuzz = constants.timeout_regular_fuzz;
127cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  }
128cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood
129cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  // If not otherwise determined, defer to a fuzz of +/-(interval / 2).
130cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  if (fuzz == 0)
131cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood    fuzz = interval;
132cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood
133cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  *next_update_check =
134cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood      *last_checked_time + FuzzedInterval(&prng, interval, fuzz);
135cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  return EvalStatus::kSucceeded;
136cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood}
137cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood
138cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron WoodTimeDelta NextUpdateCheckTimePolicyImpl::FuzzedInterval(PRNG* prng,
139cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood                                                        int interval,
140cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood                                                        int fuzz) {
141cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  DCHECK_GE(interval, 0);
142cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  DCHECK_GE(fuzz, 0);
143cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  int half_fuzz = fuzz / 2;
144cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  // This guarantees the output interval is non negative.
145cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  int interval_min = max(interval - half_fuzz, 0);
146cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  int interval_max = interval + half_fuzz;
147cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood  return TimeDelta::FromSeconds(prng->RandMinMax(interval_min, interval_max));
148cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood}
149cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood
150cdd6bab2e1071f440db5d99eece02fe7ae8acf90Aaron Wood}  // namespace chromeos_update_manager
151