// // Copyright (C) 2017 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #include "update_engine/update_manager/next_update_check_policy_impl.h" #include <algorithm> #include "update_engine/common/utils.h" using base::Time; using base::TimeDelta; using std::max; using std::string; namespace chromeos_update_manager { NextUpdateCheckTimePolicyImpl::NextUpdateCheckTimePolicyImpl( const NextUpdateCheckPolicyConstants& constants) : policy_constants_(constants) {} EvalStatus NextUpdateCheckTimePolicyImpl::UpdateCheckAllowed( EvaluationContext* ec, State* state, string* error, UpdateCheckParams* result) const { // Ensure that periodic update checks are timed properly. Time next_update_check; if (NextUpdateCheckTime( ec, state, error, &next_update_check, policy_constants_) != EvalStatus::kSucceeded) { return EvalStatus::kFailed; } if (!ec->IsWallclockTimeGreaterThan(next_update_check)) { LOG(INFO) << "Periodic check interval not satisfied, blocking until " << chromeos_update_engine::utils::ToString(next_update_check); return EvalStatus::kAskMeAgainLater; } return EvalStatus::kContinue; } EvalStatus NextUpdateCheckTimePolicyImpl::NextUpdateCheckTime( EvaluationContext* ec, State* state, string* error, Time* next_update_check, const NextUpdateCheckPolicyConstants& constants) { UpdaterProvider* const updater_provider = state->updater_provider(); // Don't check for updates too often. We limit the update checks to once every // some interval. The interval is kTimeoutInitialInterval the first time and // kTimeoutPeriodicInterval for the subsequent update checks. If the update // check fails, we increase the interval between the update checks // exponentially until kTimeoutMaxBackoffInterval. Finally, to avoid having // many chromebooks running update checks at the exact same time, we add some // fuzz to the interval. const Time* updater_started_time = ec->GetValue(updater_provider->var_updater_started_time()); POLICY_CHECK_VALUE_AND_FAIL(updater_started_time, error); const Time* last_checked_time = ec->GetValue(updater_provider->var_last_checked_time()); const auto* seed = ec->GetValue(state->random_provider()->var_seed()); POLICY_CHECK_VALUE_AND_FAIL(seed, error); PRNG prng(*seed); // If this is the first attempt, compute and return an initial value. if (last_checked_time == nullptr || *last_checked_time < *updater_started_time) { *next_update_check = *updater_started_time + FuzzedInterval(&prng, constants.timeout_initial_interval, constants.timeout_regular_fuzz); return EvalStatus::kSucceeded; } // Check whether the server is enforcing a poll interval; if not, this value // will be zero. const unsigned int* server_dictated_poll_interval = ec->GetValue(updater_provider->var_server_dictated_poll_interval()); POLICY_CHECK_VALUE_AND_FAIL(server_dictated_poll_interval, error); int interval = *server_dictated_poll_interval; int fuzz = 0; // If no poll interval was dictated by server compute a back-off period, // starting from a predetermined base periodic interval and increasing // exponentially by the number of consecutive failed attempts. if (interval == 0) { const unsigned int* consecutive_failed_update_checks = ec->GetValue(updater_provider->var_consecutive_failed_update_checks()); POLICY_CHECK_VALUE_AND_FAIL(consecutive_failed_update_checks, error); interval = constants.timeout_periodic_interval; unsigned int num_failures = *consecutive_failed_update_checks; while (interval < constants.timeout_max_backoff_interval && num_failures) { interval *= 2; num_failures--; } } // We cannot back off longer than the predetermined maximum interval. if (interval > constants.timeout_max_backoff_interval) interval = constants.timeout_max_backoff_interval; // We cannot back off shorter than the predetermined periodic interval. Also, // in this case set the fuzz to a predetermined regular value. if (interval <= constants.timeout_periodic_interval) { interval = constants.timeout_periodic_interval; fuzz = constants.timeout_regular_fuzz; } // If not otherwise determined, defer to a fuzz of +/-(interval / 2). if (fuzz == 0) fuzz = interval; *next_update_check = *last_checked_time + FuzzedInterval(&prng, interval, fuzz); return EvalStatus::kSucceeded; } TimeDelta NextUpdateCheckTimePolicyImpl::FuzzedInterval(PRNG* prng, int interval, int fuzz) { DCHECK_GE(interval, 0); DCHECK_GE(fuzz, 0); int half_fuzz = fuzz / 2; // This guarantees the output interval is non negative. int interval_min = max(interval - half_fuzz, 0); int interval_max = interval + half_fuzz; return TimeDelta::FromSeconds(prng->RandMinMax(interval_min, interval_max)); } } // namespace chromeos_update_manager