blob: f5b8f366444c1a7e178f35f0e8670427357e2eb3 [file] [log] [blame]
Jay Srinivasan6f6ea002012-12-14 11:26:28 -08001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "update_engine/payload_state.h"
6
Jay Srinivasan08262882012-12-28 19:29:43 -08007#include <algorithm>
8
Jay Srinivasan6f6ea002012-12-14 11:26:28 -08009#include <base/logging.h>
10#include <base/stringprintf.h>
11
Jay Srinivasan6f6ea002012-12-14 11:26:28 -080012#include "update_engine/prefs.h"
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -080013#include "update_engine/utils.h"
Jay Srinivasan6f6ea002012-12-14 11:26:28 -080014
Jay Srinivasan08262882012-12-28 19:29:43 -080015using base::Time;
16using base::TimeDelta;
17using std::min;
Jay Srinivasan6f6ea002012-12-14 11:26:28 -080018using std::string;
19
20namespace chromeos_update_engine {
21
Jay Srinivasan08262882012-12-28 19:29:43 -080022// We want to upperbound backoffs to 16 days
23static const uint32_t kMaxBackoffDays = 16;
Jay Srinivasan6f6ea002012-12-14 11:26:28 -080024
Jay Srinivasan08262882012-12-28 19:29:43 -080025// We want to randomize retry attempts after the backoff by +/- 6 hours.
26static const uint32_t kMaxBackoffFuzzMinutes = 12 * 60;
Jay Srinivasan6f6ea002012-12-14 11:26:28 -080027
28bool PayloadState::Initialize(PrefsInterface* prefs) {
29 CHECK(prefs);
30 prefs_ = prefs;
Jay Srinivasan08262882012-12-28 19:29:43 -080031 LoadResponseSignature();
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -080032 LoadPayloadAttemptNumber();
Jay Srinivasan6f6ea002012-12-14 11:26:28 -080033 LoadUrlIndex();
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -080034 LoadUrlFailureCount();
Jay Srinivasan08262882012-12-28 19:29:43 -080035 LoadBackoffExpiryTime();
Jay Srinivasan6f6ea002012-12-14 11:26:28 -080036 return true;
37}
38
Jay Srinivasan6f6ea002012-12-14 11:26:28 -080039void PayloadState::SetResponse(const OmahaResponse& omaha_response) {
Jay Srinivasan08262882012-12-28 19:29:43 -080040 // Always store the latest response.
41 response_ = omaha_response;
Jay Srinivasan6f6ea002012-12-14 11:26:28 -080042
Jay Srinivasan08262882012-12-28 19:29:43 -080043 // Check if the "signature" of this response (i.e. the fields we care about)
44 // has changed.
45 string new_response_signature = CalculateResponseSignature();
46 bool has_response_changed = (response_signature_ != new_response_signature);
47
48 // If the response has changed, we should persist the new signature and
49 // clear away all the existing state.
Jay Srinivasan6f6ea002012-12-14 11:26:28 -080050 if (has_response_changed) {
Jay Srinivasan08262882012-12-28 19:29:43 -080051 LOG(INFO) << "Resetting all persisted state as this is a new response";
52 SetResponseSignature(new_response_signature);
53 ResetPersistedState();
54 return;
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -080055 }
56
Jay Srinivasan08262882012-12-28 19:29:43 -080057 // This is the earliest point at which we can validate whether the URL index
58 // we loaded from the persisted state is a valid value. If the response
59 // hasn't changed but the URL index is invalid, it's indicative of some
60 // tampering of the persisted state.
61 if (url_index_ >= GetNumUrls()) {
62 LOG(INFO) << "Resetting all payload state as the url index seems to have "
63 "been tampered with";
64 ResetPersistedState();
65 return;
Jay Srinivasan6f6ea002012-12-14 11:26:28 -080066 }
67}
68
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -080069void PayloadState::DownloadComplete() {
70 LOG(INFO) << "Payload downloaded successfully";
71 IncrementPayloadAttemptNumber();
72}
73
74void PayloadState::DownloadProgress(size_t count) {
75 if (count == 0)
76 return;
77
78 // We've received non-zero bytes from a recent download operation. Since our
79 // URL failure count is meant to penalize a URL only for consecutive
80 // failures, downloading bytes successfully means we should reset the failure
81 // count (as we know at least that the URL is working). In future, we can
82 // design this to be more sophisticated to check for more intelligent failure
83 // patterns, but right now, even 1 byte downloaded will mark the URL to be
84 // good unless it hits 10 (or configured number of) consecutive failures
85 // again.
86
87 if (GetUrlFailureCount() == 0)
88 return;
89
90 LOG(INFO) << "Resetting failure count of Url" << GetUrlIndex()
91 << " to 0 as we received " << count << " bytes successfully";
92 SetUrlFailureCount(0);
93}
94
Jay Srinivasan6f6ea002012-12-14 11:26:28 -080095void PayloadState::UpdateFailed(ActionExitCode error) {
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -080096 ActionExitCode base_error = utils::GetBaseErrorCode(error);
Jay Srinivasan55f50c22013-01-10 19:24:35 -080097 LOG(INFO) << "Updating payload state for error code: " << base_error
98 << " (" << utils::CodeToString(base_error) << ")";
Jay Srinivasan6f6ea002012-12-14 11:26:28 -080099
Jay Srinivasan08262882012-12-28 19:29:43 -0800100 if (GetNumUrls() == 0) {
101 // This means we got this error even before we got a valid Omaha response.
102 // So we should not advance the url_index_ in such cases.
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800103 LOG(INFO) << "Ignoring failures until we get a valid Omaha response.";
104 return;
105 }
106
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -0800107 switch (base_error) {
108 // Errors which are good indicators of a problem with a particular URL or
109 // the protocol used in the URL or entities in the communication channel
110 // (e.g. proxies). We should try the next available URL in the next update
111 // check to quickly recover from these errors.
112 case kActionCodePayloadHashMismatchError:
113 case kActionCodePayloadSizeMismatchError:
114 case kActionCodeDownloadPayloadVerificationError:
115 case kActionCodeDownloadPayloadPubKeyVerificationError:
116 case kActionCodeSignedDeltaPayloadExpectedError:
117 case kActionCodeDownloadInvalidMetadataMagicString:
118 case kActionCodeDownloadSignatureMissingInManifest:
119 case kActionCodeDownloadManifestParseError:
120 case kActionCodeDownloadMetadataSignatureError:
121 case kActionCodeDownloadMetadataSignatureVerificationError:
122 case kActionCodeDownloadMetadataSignatureMismatch:
123 case kActionCodeDownloadOperationHashVerificationError:
124 case kActionCodeDownloadOperationExecutionError:
125 case kActionCodeDownloadOperationHashMismatch:
126 case kActionCodeDownloadInvalidMetadataSize:
127 case kActionCodeDownloadInvalidMetadataSignature:
128 case kActionCodeDownloadOperationHashMissingError:
129 case kActionCodeDownloadMetadataSignatureMissingError:
130 IncrementUrlIndex();
131 break;
132
133 // Errors which seem to be just transient network/communication related
134 // failures and do not indicate any inherent problem with the URL itself.
135 // So, we should keep the current URL but just increment the
136 // failure count to give it more chances. This way, while we maximize our
137 // chances of downloading from the URLs that appear earlier in the response
138 // (because download from a local server URL that appears earlier in a
139 // response is preferable than downloading from the next URL which could be
140 // a internet URL and thus could be more expensive).
141 case kActionCodeError:
142 case kActionCodeDownloadTransferError:
143 case kActionCodeDownloadWriteError:
144 case kActionCodeDownloadStateInitializationError:
145 case kActionCodeOmahaErrorInHTTPResponse: // Aggregate code for HTTP errors.
146 IncrementFailureCount();
147 break;
148
149 // Errors which are not specific to a URL and hence shouldn't result in
150 // the URL being penalized. This can happen in two cases:
151 // 1. We haven't started downloading anything: These errors don't cost us
152 // anything in terms of actual payload bytes, so we should just do the
153 // regular retries at the next update check.
154 // 2. We have successfully downloaded the payload: In this case, the
155 // payload attempt number would have been incremented and would take care
Jay Srinivasan08262882012-12-28 19:29:43 -0800156 // of the backoff at the next update check.
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -0800157 // In either case, there's no need to update URL index or failure count.
158 case kActionCodeOmahaRequestError:
159 case kActionCodeOmahaResponseHandlerError:
160 case kActionCodePostinstallRunnerError:
161 case kActionCodeFilesystemCopierError:
162 case kActionCodeInstallDeviceOpenError:
163 case kActionCodeKernelDeviceOpenError:
164 case kActionCodeDownloadNewPartitionInfoError:
165 case kActionCodeNewRootfsVerificationError:
166 case kActionCodeNewKernelVerificationError:
167 case kActionCodePostinstallBootedFromFirmwareB:
168 case kActionCodeOmahaRequestEmptyResponseError:
169 case kActionCodeOmahaRequestXMLParseError:
170 case kActionCodeOmahaResponseInvalid:
171 case kActionCodeOmahaUpdateIgnoredPerPolicy:
172 case kActionCodeOmahaUpdateDeferredPerPolicy:
Jay Srinivasan08262882012-12-28 19:29:43 -0800173 case kActionCodeOmahaUpdateDeferredForBackoff:
Jay Srinivasanae4697c2013-03-18 17:08:08 -0700174 case kActionCodePostinstallPowerwashError:
Jay Srinivasan1c0fe792013-03-28 16:45:25 -0700175 case kActionCodeUpdateCanceledByChannelChange:
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -0800176 LOG(INFO) << "Not incrementing URL index or failure count for this error";
177 break;
178
179 case kActionCodeSuccess: // success code
180 case kActionCodeSetBootableFlagError: // unused
181 case kActionCodeUmaReportedMax: // not an error code
182 case kActionCodeOmahaRequestHTTPResponseBase: // aggregated already
Jay Srinivasan55f50c22013-01-10 19:24:35 -0800183 case kActionCodeDevModeFlag: // not an error code
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -0800184 case kActionCodeResumedFlag: // not an error code
Jay Srinivasan55f50c22013-01-10 19:24:35 -0800185 case kActionCodeTestImageFlag: // not an error code
186 case kActionCodeTestOmahaUrlFlag: // not an error code
187 case kSpecialFlags: // not an error code
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -0800188 // These shouldn't happen. Enumerating these explicitly here so that we
189 // can let the compiler warn about new error codes that are added to
190 // action_processor.h but not added here.
191 LOG(WARNING) << "Unexpected error code for UpdateFailed";
192 break;
193
194 // Note: Not adding a default here so as to let the compiler warn us of
195 // any new enums that were added in the .h but not listed in this switch.
196 }
197}
198
Jay Srinivasan08262882012-12-28 19:29:43 -0800199bool PayloadState::ShouldBackoffDownload() {
200 if (response_.disable_payload_backoff) {
201 LOG(INFO) << "Payload backoff logic is disabled. "
202 "Can proceed with the download";
203 return false;
204 }
205
206 if (response_.is_delta_payload) {
207 // If delta payloads fail, we want to fallback quickly to full payloads as
208 // they are more likely to succeed. Exponential backoffs would greatly
209 // slow down the fallback to full payloads. So we don't backoff for delta
210 // payloads.
211 LOG(INFO) << "No backoffs for delta payloads. "
212 << "Can proceed with the download";
213 return false;
214 }
215
216 if (!utils::IsOfficialBuild()) {
217 // Backoffs are needed only for official builds. We do not want any delays
218 // or update failures due to backoffs during testing or development.
219 LOG(INFO) << "No backoffs for test/dev images. "
220 << "Can proceed with the download";
221 return false;
222 }
223
224 if (backoff_expiry_time_.is_null()) {
225 LOG(INFO) << "No backoff expiry time has been set. "
226 << "Can proceed with the download";
227 return false;
228 }
229
230 if (backoff_expiry_time_ < Time::Now()) {
231 LOG(INFO) << "The backoff expiry time ("
232 << utils::ToString(backoff_expiry_time_)
233 << ") has elapsed. Can proceed with the download";
234 return false;
235 }
236
237 LOG(INFO) << "Cannot proceed with downloads as we need to backoff until "
238 << utils::ToString(backoff_expiry_time_);
239 return true;
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -0800240}
241
242void PayloadState::IncrementPayloadAttemptNumber() {
Jay Srinivasan08262882012-12-28 19:29:43 -0800243 if (response_.is_delta_payload) {
244 LOG(INFO) << "Not incrementing payload attempt number for delta payloads";
245 return;
246 }
247
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -0800248 LOG(INFO) << "Incrementing the payload attempt number";
249 SetPayloadAttemptNumber(GetPayloadAttemptNumber() + 1);
Jay Srinivasan08262882012-12-28 19:29:43 -0800250 UpdateBackoffExpiryTime();
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -0800251}
252
253void PayloadState::IncrementUrlIndex() {
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800254 uint32_t next_url_index = GetUrlIndex() + 1;
Jay Srinivasan08262882012-12-28 19:29:43 -0800255 if (next_url_index < GetNumUrls()) {
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -0800256 LOG(INFO) << "Incrementing the URL index for next attempt";
257 SetUrlIndex(next_url_index);
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800258 } else {
259 LOG(INFO) << "Resetting the current URL index (" << GetUrlIndex() << ") to "
Jay Srinivasan08262882012-12-28 19:29:43 -0800260 << "0 as we only have " << GetNumUrls() << " URL(s)";
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -0800261 SetUrlIndex(0);
262 IncrementPayloadAttemptNumber();
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800263 }
Jay Srinivasan08262882012-12-28 19:29:43 -0800264
265 // Whenever we update the URL index, we should also clear the URL failure
266 // count so we can start over fresh for the new URL.
267 SetUrlFailureCount(0);
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800268}
269
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -0800270void PayloadState::IncrementFailureCount() {
271 uint32_t next_url_failure_count = GetUrlFailureCount() + 1;
Jay Srinivasan08262882012-12-28 19:29:43 -0800272 if (next_url_failure_count < response_.max_failure_count_per_url) {
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -0800273 LOG(INFO) << "Incrementing the URL failure count";
274 SetUrlFailureCount(next_url_failure_count);
275 } else {
276 LOG(INFO) << "Reached max number of failures for Url" << GetUrlIndex()
277 << ". Trying next available URL";
278 IncrementUrlIndex();
279 }
280}
281
Jay Srinivasan08262882012-12-28 19:29:43 -0800282void PayloadState::UpdateBackoffExpiryTime() {
283 if (response_.disable_payload_backoff) {
284 LOG(INFO) << "Resetting backoff expiry time as payload backoff is disabled";
285 SetBackoffExpiryTime(Time());
286 return;
287 }
288
289 if (GetPayloadAttemptNumber() == 0) {
290 SetBackoffExpiryTime(Time());
291 return;
292 }
293
294 // Since we're doing left-shift below, make sure we don't shift more
295 // than this. E.g. if uint32_t is 4-bytes, don't left-shift more than 30 bits,
296 // since we don't expect value of kMaxBackoffDays to be more than 100 anyway.
297 uint32_t num_days = 1; // the value to be shifted.
298 const uint32_t kMaxShifts = (sizeof(num_days) * 8) - 2;
299
300 // Normal backoff days is 2 raised to (payload_attempt_number - 1).
301 // E.g. if payload_attempt_number is over 30, limit power to 30.
302 uint32_t power = min(GetPayloadAttemptNumber() - 1, kMaxShifts);
303
304 // The number of days is the minimum of 2 raised to (payload_attempt_number
305 // - 1) or kMaxBackoffDays.
306 num_days = min(num_days << power, kMaxBackoffDays);
307
308 // We don't want all retries to happen exactly at the same time when
309 // retrying after backoff. So add some random minutes to fuzz.
310 int fuzz_minutes = utils::FuzzInt(0, kMaxBackoffFuzzMinutes);
311 TimeDelta next_backoff_interval = TimeDelta::FromDays(num_days) +
312 TimeDelta::FromMinutes(fuzz_minutes);
313 LOG(INFO) << "Incrementing the backoff expiry time by "
314 << utils::FormatTimeDelta(next_backoff_interval);
315 SetBackoffExpiryTime(Time::Now() + next_backoff_interval);
316}
317
318void PayloadState::ResetPersistedState() {
319 SetPayloadAttemptNumber(0);
320 SetUrlIndex(0);
321 SetUrlFailureCount(0);
322 UpdateBackoffExpiryTime(); // This will reset the backoff expiry time.
323}
324
325string PayloadState::CalculateResponseSignature() {
326 string response_sign = StringPrintf("NumURLs = %d\n",
327 response_.payload_urls.size());
328
329 for (size_t i = 0; i < response_.payload_urls.size(); i++)
330 response_sign += StringPrintf("Url%d = %s\n",
331 i, response_.payload_urls[i].c_str());
332
333 response_sign += StringPrintf("Payload Size = %llu\n"
334 "Payload Sha256 Hash = %s\n"
335 "Metadata Size = %llu\n"
336 "Metadata Signature = %s\n"
337 "Is Delta Payload = %d\n"
338 "Max Failure Count Per Url = %d\n"
339 "Disable Payload Backoff = %d\n",
340 response_.size,
341 response_.hash.c_str(),
342 response_.metadata_size,
343 response_.metadata_signature.c_str(),
344 response_.is_delta_payload,
345 response_.max_failure_count_per_url,
346 response_.disable_payload_backoff);
347 return response_sign;
348}
349
350void PayloadState::LoadResponseSignature() {
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800351 CHECK(prefs_);
352 string stored_value;
Jay Srinivasan08262882012-12-28 19:29:43 -0800353 if (prefs_->Exists(kPrefsCurrentResponseSignature) &&
354 prefs_->GetString(kPrefsCurrentResponseSignature, &stored_value)) {
355 SetResponseSignature(stored_value);
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800356 }
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800357}
358
Jay Srinivasan08262882012-12-28 19:29:43 -0800359void PayloadState::SetResponseSignature(string response_signature) {
360 CHECK(prefs_);
361 response_signature_ = response_signature;
362 LOG(INFO) << "Current Response Signature = \n" << response_signature_;
363 prefs_->SetString(kPrefsCurrentResponseSignature, response_signature_);
364}
365
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -0800366void PayloadState::LoadPayloadAttemptNumber() {
367 CHECK(prefs_);
368 int64_t stored_value;
369 if (prefs_->Exists(kPrefsPayloadAttemptNumber) &&
370 prefs_->GetInt64(kPrefsPayloadAttemptNumber, &stored_value)) {
371 if (stored_value < 0) {
372 LOG(ERROR) << "Invalid payload attempt number (" << stored_value
373 << ") in persisted state. Defaulting to 0";
374 stored_value = 0;
375 }
Jay Srinivasan08262882012-12-28 19:29:43 -0800376 SetPayloadAttemptNumber(stored_value);
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -0800377 }
378}
379
380void PayloadState::SetPayloadAttemptNumber(uint32_t payload_attempt_number) {
381 CHECK(prefs_);
382 payload_attempt_number_ = payload_attempt_number;
383 LOG(INFO) << "Payload Attempt Number = " << payload_attempt_number_;
384 prefs_->SetInt64(kPrefsPayloadAttemptNumber, payload_attempt_number_);
385}
386
387void PayloadState::LoadUrlIndex() {
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800388 CHECK(prefs_);
389 int64_t stored_value;
390 if (prefs_->Exists(kPrefsCurrentUrlIndex) &&
391 prefs_->GetInt64(kPrefsCurrentUrlIndex, &stored_value)) {
Jay Srinivasan08262882012-12-28 19:29:43 -0800392 // We only check for basic sanity value here. Detailed check will be
393 // done in SetResponse once the first response comes in.
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -0800394 if (stored_value < 0) {
395 LOG(ERROR) << "Invalid URL Index (" << stored_value
396 << ") in persisted state. Defaulting to 0";
397 stored_value = 0;
398 }
Jay Srinivasan08262882012-12-28 19:29:43 -0800399 SetUrlIndex(stored_value);
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800400 }
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800401}
402
403void PayloadState::SetUrlIndex(uint32_t url_index) {
404 CHECK(prefs_);
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800405 url_index_ = url_index;
406 LOG(INFO) << "Current URL Index = " << url_index_;
407 prefs_->SetInt64(kPrefsCurrentUrlIndex, url_index_);
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -0800408}
409
410void PayloadState::LoadUrlFailureCount() {
411 CHECK(prefs_);
412 int64_t stored_value;
413 if (prefs_->Exists(kPrefsCurrentUrlFailureCount) &&
414 prefs_->GetInt64(kPrefsCurrentUrlFailureCount, &stored_value)) {
415 if (stored_value < 0) {
416 LOG(ERROR) << "Invalid URL Failure count (" << stored_value
417 << ") in persisted state. Defaulting to 0";
418 stored_value = 0;
419 }
Jay Srinivasan08262882012-12-28 19:29:43 -0800420 SetUrlFailureCount(stored_value);
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -0800421 }
422}
423
424void PayloadState::SetUrlFailureCount(uint32_t url_failure_count) {
425 CHECK(prefs_);
426 url_failure_count_ = url_failure_count;
427 LOG(INFO) << "Current URL (Url" << GetUrlIndex()
428 << ")'s Failure Count = " << url_failure_count_;
429 prefs_->SetInt64(kPrefsCurrentUrlFailureCount, url_failure_count_);
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800430}
431
Jay Srinivasan08262882012-12-28 19:29:43 -0800432void PayloadState::LoadBackoffExpiryTime() {
433 CHECK(prefs_);
434 int64_t stored_value;
435 if (!prefs_->Exists(kPrefsBackoffExpiryTime))
436 return;
437
438 if (!prefs_->GetInt64(kPrefsBackoffExpiryTime, &stored_value))
439 return;
440
441 Time stored_time = Time::FromInternalValue(stored_value);
442 if (stored_time > Time::Now() + TimeDelta::FromDays(kMaxBackoffDays)) {
443 LOG(ERROR) << "Invalid backoff expiry time ("
444 << utils::ToString(stored_time)
445 << ") in persisted state. Resetting.";
446 stored_time = Time();
447 }
448 SetBackoffExpiryTime(stored_time);
449}
450
451void PayloadState::SetBackoffExpiryTime(const Time& new_time) {
452 CHECK(prefs_);
453 backoff_expiry_time_ = new_time;
454 LOG(INFO) << "Backoff Expiry Time = "
455 << utils::ToString(backoff_expiry_time_);
456 prefs_->SetInt64(kPrefsBackoffExpiryTime,
457 backoff_expiry_time_.ToInternalValue());
458}
459
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800460} // namespace chromeos_update_engine