Home | History | Annotate | Download | only in update_manager
      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 
     30 NextUpdateCheckTimePolicyImpl::NextUpdateCheckTimePolicyImpl(
     31     const NextUpdateCheckPolicyConstants& constants)
     32     : policy_constants_(constants) {}
     33 
     34 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 
     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   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 
    138 TimeDelta 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
    151