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