blob: 3de9e04227768afefbe025398437654e5914d074 [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>
Jay Srinivasan19409b72013-04-12 19:23:36 -070010#include "base/string_util.h"
Jay Srinivasan6f6ea002012-12-14 11:26:28 -080011#include <base/stringprintf.h>
12
David Zeuthenf413fe52013-04-22 14:04:39 -070013#include "update_engine/clock.h"
Jay Srinivasand29695d2013-04-08 15:08:05 -070014#include "update_engine/constants.h"
Jay Srinivasan19409b72013-04-12 19:23:36 -070015#include "update_engine/prefs.h"
16#include "update_engine/system_state.h"
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -080017#include "update_engine/utils.h"
Jay Srinivasan6f6ea002012-12-14 11:26:28 -080018
Jay Srinivasan08262882012-12-28 19:29:43 -080019using base::Time;
20using base::TimeDelta;
21using std::min;
Jay Srinivasan6f6ea002012-12-14 11:26:28 -080022using std::string;
23
24namespace chromeos_update_engine {
25
David Zeuthen9a017f22013-04-11 16:10:26 -070026const TimeDelta PayloadState::kDurationSlack = TimeDelta::FromSeconds(600);
27
Jay Srinivasan08262882012-12-28 19:29:43 -080028// We want to upperbound backoffs to 16 days
29static const uint32_t kMaxBackoffDays = 16;
Jay Srinivasan6f6ea002012-12-14 11:26:28 -080030
Jay Srinivasan08262882012-12-28 19:29:43 -080031// We want to randomize retry attempts after the backoff by +/- 6 hours.
32static const uint32_t kMaxBackoffFuzzMinutes = 12 * 60;
Jay Srinivasan6f6ea002012-12-14 11:26:28 -080033
Jay Srinivasan19409b72013-04-12 19:23:36 -070034PayloadState::PayloadState()
35 : prefs_(NULL),
36 payload_attempt_number_(0),
37 url_index_(0),
David Zeuthencc6f9962013-04-18 11:57:24 -070038 url_failure_count_(0),
39 url_switch_count_(0) {
Jay Srinivasan19409b72013-04-12 19:23:36 -070040 for (int i = 0; i <= kNumDownloadSources; i++)
41 total_bytes_downloaded_[i] = current_bytes_downloaded_[i] = 0;
42}
43
44bool PayloadState::Initialize(SystemState* system_state) {
45 system_state_ = system_state;
46 prefs_ = system_state_->prefs();
Jay Srinivasan08262882012-12-28 19:29:43 -080047 LoadResponseSignature();
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -080048 LoadPayloadAttemptNumber();
Jay Srinivasan6f6ea002012-12-14 11:26:28 -080049 LoadUrlIndex();
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -080050 LoadUrlFailureCount();
David Zeuthencc6f9962013-04-18 11:57:24 -070051 LoadUrlSwitchCount();
Jay Srinivasan08262882012-12-28 19:29:43 -080052 LoadBackoffExpiryTime();
David Zeuthen9a017f22013-04-11 16:10:26 -070053 LoadUpdateTimestampStart();
54 // The LoadUpdateDurationUptime() method relies on LoadUpdateTimestampStart()
55 // being called before it. Don't reorder.
56 LoadUpdateDurationUptime();
Jay Srinivasan19409b72013-04-12 19:23:36 -070057 for (int i = 0; i < kNumDownloadSources; i++) {
58 DownloadSource source = static_cast<DownloadSource>(i);
59 LoadCurrentBytesDownloaded(source);
60 LoadTotalBytesDownloaded(source);
61 }
Chris Sosabe45bef2013-04-09 18:25:12 -070062 LoadNumReboots();
Jay Srinivasan6f6ea002012-12-14 11:26:28 -080063 return true;
64}
65
Jay Srinivasan6f6ea002012-12-14 11:26:28 -080066void PayloadState::SetResponse(const OmahaResponse& omaha_response) {
Jay Srinivasan08262882012-12-28 19:29:43 -080067 // Always store the latest response.
68 response_ = omaha_response;
Jay Srinivasan6f6ea002012-12-14 11:26:28 -080069
Jay Srinivasan53173b92013-05-17 17:13:01 -070070 // Compute the candidate URLs first as they are used to calculate the
71 // response signature so that a change in enterprise policy for
72 // HTTP downloads being enabled or not could be honored as soon as the
73 // next update check happens.
74 ComputeCandidateUrls();
75
Jay Srinivasan08262882012-12-28 19:29:43 -080076 // Check if the "signature" of this response (i.e. the fields we care about)
77 // has changed.
78 string new_response_signature = CalculateResponseSignature();
79 bool has_response_changed = (response_signature_ != new_response_signature);
80
81 // If the response has changed, we should persist the new signature and
82 // clear away all the existing state.
Jay Srinivasan6f6ea002012-12-14 11:26:28 -080083 if (has_response_changed) {
Jay Srinivasan08262882012-12-28 19:29:43 -080084 LOG(INFO) << "Resetting all persisted state as this is a new response";
85 SetResponseSignature(new_response_signature);
86 ResetPersistedState();
87 return;
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -080088 }
89
Jay Srinivasan08262882012-12-28 19:29:43 -080090 // This is the earliest point at which we can validate whether the URL index
91 // we loaded from the persisted state is a valid value. If the response
92 // hasn't changed but the URL index is invalid, it's indicative of some
93 // tampering of the persisted state.
Jay Srinivasan53173b92013-05-17 17:13:01 -070094 if (static_cast<uint32_t>(url_index_) >= candidate_urls_.size()) {
Jay Srinivasan08262882012-12-28 19:29:43 -080095 LOG(INFO) << "Resetting all payload state as the url index seems to have "
96 "been tampered with";
97 ResetPersistedState();
98 return;
Jay Srinivasan6f6ea002012-12-14 11:26:28 -080099 }
Jay Srinivasan19409b72013-04-12 19:23:36 -0700100
101 // Update the current download source which depends on the latest value of
102 // the response.
103 UpdateCurrentDownloadSource();
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800104}
105
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -0800106void PayloadState::DownloadComplete() {
107 LOG(INFO) << "Payload downloaded successfully";
108 IncrementPayloadAttemptNumber();
109}
110
111void PayloadState::DownloadProgress(size_t count) {
112 if (count == 0)
113 return;
114
David Zeuthen9a017f22013-04-11 16:10:26 -0700115 CalculateUpdateDurationUptime();
Jay Srinivasan19409b72013-04-12 19:23:36 -0700116 UpdateBytesDownloaded(count);
David Zeuthen9a017f22013-04-11 16:10:26 -0700117
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -0800118 // We've received non-zero bytes from a recent download operation. Since our
119 // URL failure count is meant to penalize a URL only for consecutive
120 // failures, downloading bytes successfully means we should reset the failure
121 // count (as we know at least that the URL is working). In future, we can
122 // design this to be more sophisticated to check for more intelligent failure
123 // patterns, but right now, even 1 byte downloaded will mark the URL to be
124 // good unless it hits 10 (or configured number of) consecutive failures
125 // again.
126
127 if (GetUrlFailureCount() == 0)
128 return;
129
130 LOG(INFO) << "Resetting failure count of Url" << GetUrlIndex()
131 << " to 0 as we received " << count << " bytes successfully";
132 SetUrlFailureCount(0);
133}
134
Chris Sosabe45bef2013-04-09 18:25:12 -0700135void PayloadState::UpdateResumed() {
136 LOG(INFO) << "Resuming an update that was previously started.";
137 UpdateNumReboots();
138}
139
Jay Srinivasan19409b72013-04-12 19:23:36 -0700140void PayloadState::UpdateRestarted() {
141 LOG(INFO) << "Starting a new update";
142 ResetDownloadSourcesOnNewUpdate();
Chris Sosabe45bef2013-04-09 18:25:12 -0700143 SetNumReboots(0);
Jay Srinivasan19409b72013-04-12 19:23:36 -0700144}
145
David Zeuthen9a017f22013-04-11 16:10:26 -0700146void PayloadState::UpdateSucceeded() {
Jay Srinivasan19409b72013-04-12 19:23:36 -0700147 // Send the relevant metrics that are tracked in this class to UMA.
David Zeuthen9a017f22013-04-11 16:10:26 -0700148 CalculateUpdateDurationUptime();
David Zeuthenf413fe52013-04-22 14:04:39 -0700149 SetUpdateTimestampEnd(system_state_->clock()->GetWallclockTime());
Jay Srinivasan19409b72013-04-12 19:23:36 -0700150 ReportBytesDownloadedMetrics();
David Zeuthencc6f9962013-04-18 11:57:24 -0700151 ReportUpdateUrlSwitchesMetric();
Chris Sosabe45bef2013-04-09 18:25:12 -0700152 ReportRebootMetrics();
David Zeuthen674c3182013-04-18 14:05:20 -0700153 ReportDurationMetrics();
David Zeuthen9a017f22013-04-11 16:10:26 -0700154}
155
David Zeuthena99981f2013-04-29 13:42:47 -0700156void PayloadState::UpdateFailed(ErrorCode error) {
157 ErrorCode base_error = utils::GetBaseErrorCode(error);
Jay Srinivasan55f50c22013-01-10 19:24:35 -0800158 LOG(INFO) << "Updating payload state for error code: " << base_error
159 << " (" << utils::CodeToString(base_error) << ")";
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800160
Jay Srinivasan53173b92013-05-17 17:13:01 -0700161 if (candidate_urls_.size() == 0) {
162 // This means we got this error even before we got a valid Omaha response
163 // or don't have any valid candidates in the Omaha response.
Jay Srinivasan08262882012-12-28 19:29:43 -0800164 // So we should not advance the url_index_ in such cases.
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800165 LOG(INFO) << "Ignoring failures until we get a valid Omaha response.";
166 return;
167 }
168
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -0800169 switch (base_error) {
170 // Errors which are good indicators of a problem with a particular URL or
171 // the protocol used in the URL or entities in the communication channel
172 // (e.g. proxies). We should try the next available URL in the next update
173 // check to quickly recover from these errors.
David Zeuthena99981f2013-04-29 13:42:47 -0700174 case kErrorCodePayloadHashMismatchError:
175 case kErrorCodePayloadSizeMismatchError:
176 case kErrorCodeDownloadPayloadVerificationError:
177 case kErrorCodeDownloadPayloadPubKeyVerificationError:
178 case kErrorCodeSignedDeltaPayloadExpectedError:
179 case kErrorCodeDownloadInvalidMetadataMagicString:
180 case kErrorCodeDownloadSignatureMissingInManifest:
181 case kErrorCodeDownloadManifestParseError:
182 case kErrorCodeDownloadMetadataSignatureError:
183 case kErrorCodeDownloadMetadataSignatureVerificationError:
184 case kErrorCodeDownloadMetadataSignatureMismatch:
185 case kErrorCodeDownloadOperationHashVerificationError:
186 case kErrorCodeDownloadOperationExecutionError:
187 case kErrorCodeDownloadOperationHashMismatch:
188 case kErrorCodeDownloadInvalidMetadataSize:
189 case kErrorCodeDownloadInvalidMetadataSignature:
190 case kErrorCodeDownloadOperationHashMissingError:
191 case kErrorCodeDownloadMetadataSignatureMissingError:
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -0800192 IncrementUrlIndex();
193 break;
194
195 // Errors which seem to be just transient network/communication related
196 // failures and do not indicate any inherent problem with the URL itself.
197 // So, we should keep the current URL but just increment the
198 // failure count to give it more chances. This way, while we maximize our
199 // chances of downloading from the URLs that appear earlier in the response
200 // (because download from a local server URL that appears earlier in a
201 // response is preferable than downloading from the next URL which could be
202 // a internet URL and thus could be more expensive).
David Zeuthena99981f2013-04-29 13:42:47 -0700203 case kErrorCodeError:
204 case kErrorCodeDownloadTransferError:
205 case kErrorCodeDownloadWriteError:
206 case kErrorCodeDownloadStateInitializationError:
207 case kErrorCodeOmahaErrorInHTTPResponse: // Aggregate code for HTTP errors.
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -0800208 IncrementFailureCount();
209 break;
210
211 // Errors which are not specific to a URL and hence shouldn't result in
212 // the URL being penalized. This can happen in two cases:
213 // 1. We haven't started downloading anything: These errors don't cost us
214 // anything in terms of actual payload bytes, so we should just do the
215 // regular retries at the next update check.
216 // 2. We have successfully downloaded the payload: In this case, the
217 // payload attempt number would have been incremented and would take care
Jay Srinivasan08262882012-12-28 19:29:43 -0800218 // of the backoff at the next update check.
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -0800219 // In either case, there's no need to update URL index or failure count.
David Zeuthena99981f2013-04-29 13:42:47 -0700220 case kErrorCodeOmahaRequestError:
221 case kErrorCodeOmahaResponseHandlerError:
222 case kErrorCodePostinstallRunnerError:
223 case kErrorCodeFilesystemCopierError:
224 case kErrorCodeInstallDeviceOpenError:
225 case kErrorCodeKernelDeviceOpenError:
226 case kErrorCodeDownloadNewPartitionInfoError:
227 case kErrorCodeNewRootfsVerificationError:
228 case kErrorCodeNewKernelVerificationError:
229 case kErrorCodePostinstallBootedFromFirmwareB:
230 case kErrorCodeOmahaRequestEmptyResponseError:
231 case kErrorCodeOmahaRequestXMLParseError:
232 case kErrorCodeOmahaResponseInvalid:
233 case kErrorCodeOmahaUpdateIgnoredPerPolicy:
234 case kErrorCodeOmahaUpdateDeferredPerPolicy:
235 case kErrorCodeOmahaUpdateDeferredForBackoff:
236 case kErrorCodePostinstallPowerwashError:
237 case kErrorCodeUpdateCanceledByChannelChange:
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -0800238 LOG(INFO) << "Not incrementing URL index or failure count for this error";
239 break;
240
David Zeuthena99981f2013-04-29 13:42:47 -0700241 case kErrorCodeSuccess: // success code
242 case kErrorCodeSetBootableFlagError: // unused
243 case kErrorCodeUmaReportedMax: // not an error code
244 case kErrorCodeOmahaRequestHTTPResponseBase: // aggregated already
245 case kErrorCodeDevModeFlag: // not an error code
246 case kErrorCodeResumedFlag: // not an error code
247 case kErrorCodeTestImageFlag: // not an error code
248 case kErrorCodeTestOmahaUrlFlag: // not an error code
249 case kErrorCodeSpecialFlags: // not an error code
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -0800250 // These shouldn't happen. Enumerating these explicitly here so that we
251 // can let the compiler warn about new error codes that are added to
252 // action_processor.h but not added here.
253 LOG(WARNING) << "Unexpected error code for UpdateFailed";
254 break;
255
256 // Note: Not adding a default here so as to let the compiler warn us of
257 // any new enums that were added in the .h but not listed in this switch.
258 }
259}
260
Jay Srinivasan08262882012-12-28 19:29:43 -0800261bool PayloadState::ShouldBackoffDownload() {
262 if (response_.disable_payload_backoff) {
263 LOG(INFO) << "Payload backoff logic is disabled. "
264 "Can proceed with the download";
265 return false;
266 }
267
268 if (response_.is_delta_payload) {
269 // If delta payloads fail, we want to fallback quickly to full payloads as
270 // they are more likely to succeed. Exponential backoffs would greatly
271 // slow down the fallback to full payloads. So we don't backoff for delta
272 // payloads.
273 LOG(INFO) << "No backoffs for delta payloads. "
274 << "Can proceed with the download";
275 return false;
276 }
277
278 if (!utils::IsOfficialBuild()) {
279 // Backoffs are needed only for official builds. We do not want any delays
280 // or update failures due to backoffs during testing or development.
281 LOG(INFO) << "No backoffs for test/dev images. "
282 << "Can proceed with the download";
283 return false;
284 }
285
286 if (backoff_expiry_time_.is_null()) {
287 LOG(INFO) << "No backoff expiry time has been set. "
288 << "Can proceed with the download";
289 return false;
290 }
291
292 if (backoff_expiry_time_ < Time::Now()) {
293 LOG(INFO) << "The backoff expiry time ("
294 << utils::ToString(backoff_expiry_time_)
295 << ") has elapsed. Can proceed with the download";
296 return false;
297 }
298
299 LOG(INFO) << "Cannot proceed with downloads as we need to backoff until "
300 << utils::ToString(backoff_expiry_time_);
301 return true;
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -0800302}
303
304void PayloadState::IncrementPayloadAttemptNumber() {
Jay Srinivasan08262882012-12-28 19:29:43 -0800305 if (response_.is_delta_payload) {
306 LOG(INFO) << "Not incrementing payload attempt number for delta payloads";
307 return;
308 }
309
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -0800310 LOG(INFO) << "Incrementing the payload attempt number";
311 SetPayloadAttemptNumber(GetPayloadAttemptNumber() + 1);
Jay Srinivasan08262882012-12-28 19:29:43 -0800312 UpdateBackoffExpiryTime();
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -0800313}
314
315void PayloadState::IncrementUrlIndex() {
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800316 uint32_t next_url_index = GetUrlIndex() + 1;
Jay Srinivasan53173b92013-05-17 17:13:01 -0700317 if (next_url_index < candidate_urls_.size()) {
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -0800318 LOG(INFO) << "Incrementing the URL index for next attempt";
319 SetUrlIndex(next_url_index);
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800320 } else {
321 LOG(INFO) << "Resetting the current URL index (" << GetUrlIndex() << ") to "
Jay Srinivasan53173b92013-05-17 17:13:01 -0700322 << "0 as we only have " << candidate_urls_.size()
323 << " candidate URL(s)";
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -0800324 SetUrlIndex(0);
325 IncrementPayloadAttemptNumber();
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800326 }
Jay Srinivasan08262882012-12-28 19:29:43 -0800327
David Zeuthencc6f9962013-04-18 11:57:24 -0700328 // If we have multiple URLs, record that we just switched to another one
Jay Srinivasan53173b92013-05-17 17:13:01 -0700329 if (candidate_urls_.size() > 1)
David Zeuthencc6f9962013-04-18 11:57:24 -0700330 SetUrlSwitchCount(url_switch_count_ + 1);
331
Jay Srinivasan08262882012-12-28 19:29:43 -0800332 // Whenever we update the URL index, we should also clear the URL failure
333 // count so we can start over fresh for the new URL.
334 SetUrlFailureCount(0);
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800335}
336
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -0800337void PayloadState::IncrementFailureCount() {
338 uint32_t next_url_failure_count = GetUrlFailureCount() + 1;
Jay Srinivasan08262882012-12-28 19:29:43 -0800339 if (next_url_failure_count < response_.max_failure_count_per_url) {
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -0800340 LOG(INFO) << "Incrementing the URL failure count";
341 SetUrlFailureCount(next_url_failure_count);
342 } else {
343 LOG(INFO) << "Reached max number of failures for Url" << GetUrlIndex()
344 << ". Trying next available URL";
345 IncrementUrlIndex();
346 }
347}
348
Jay Srinivasan08262882012-12-28 19:29:43 -0800349void PayloadState::UpdateBackoffExpiryTime() {
350 if (response_.disable_payload_backoff) {
351 LOG(INFO) << "Resetting backoff expiry time as payload backoff is disabled";
352 SetBackoffExpiryTime(Time());
353 return;
354 }
355
356 if (GetPayloadAttemptNumber() == 0) {
357 SetBackoffExpiryTime(Time());
358 return;
359 }
360
361 // Since we're doing left-shift below, make sure we don't shift more
362 // than this. E.g. if uint32_t is 4-bytes, don't left-shift more than 30 bits,
363 // since we don't expect value of kMaxBackoffDays to be more than 100 anyway.
364 uint32_t num_days = 1; // the value to be shifted.
365 const uint32_t kMaxShifts = (sizeof(num_days) * 8) - 2;
366
367 // Normal backoff days is 2 raised to (payload_attempt_number - 1).
368 // E.g. if payload_attempt_number is over 30, limit power to 30.
369 uint32_t power = min(GetPayloadAttemptNumber() - 1, kMaxShifts);
370
371 // The number of days is the minimum of 2 raised to (payload_attempt_number
372 // - 1) or kMaxBackoffDays.
373 num_days = min(num_days << power, kMaxBackoffDays);
374
375 // We don't want all retries to happen exactly at the same time when
376 // retrying after backoff. So add some random minutes to fuzz.
377 int fuzz_minutes = utils::FuzzInt(0, kMaxBackoffFuzzMinutes);
378 TimeDelta next_backoff_interval = TimeDelta::FromDays(num_days) +
379 TimeDelta::FromMinutes(fuzz_minutes);
380 LOG(INFO) << "Incrementing the backoff expiry time by "
381 << utils::FormatTimeDelta(next_backoff_interval);
382 SetBackoffExpiryTime(Time::Now() + next_backoff_interval);
383}
384
Jay Srinivasan19409b72013-04-12 19:23:36 -0700385void PayloadState::UpdateCurrentDownloadSource() {
386 current_download_source_ = kNumDownloadSources;
387
Jay Srinivasan53173b92013-05-17 17:13:01 -0700388 if (GetUrlIndex() < candidate_urls_.size()) {
389 string current_url = candidate_urls_[GetUrlIndex()];
Jay Srinivasan19409b72013-04-12 19:23:36 -0700390 if (StartsWithASCII(current_url, "https://", false))
391 current_download_source_ = kDownloadSourceHttpsServer;
392 else if (StartsWithASCII(current_url, "http://", false))
393 current_download_source_ = kDownloadSourceHttpServer;
394 }
395
396 LOG(INFO) << "Current download source: "
397 << utils::ToString(current_download_source_);
398}
399
400void PayloadState::UpdateBytesDownloaded(size_t count) {
401 SetCurrentBytesDownloaded(
402 current_download_source_,
403 GetCurrentBytesDownloaded(current_download_source_) + count,
404 false);
405 SetTotalBytesDownloaded(
406 current_download_source_,
407 GetTotalBytesDownloaded(current_download_source_) + count,
408 false);
409}
410
411void PayloadState::ReportBytesDownloadedMetrics() {
412 // Report metrics collected from all known download sources to UMA.
413 // The reported data is in Megabytes in order to represent a larger
414 // sample range.
Jay Srinivasandbd9ea22013-04-22 17:45:19 -0700415 int download_sources_used = 0;
416 string metric;
417 uint64_t successful_mbs = 0;
418 uint64_t total_mbs = 0;
Jay Srinivasan19409b72013-04-12 19:23:36 -0700419 for (int i = 0; i < kNumDownloadSources; i++) {
420 DownloadSource source = static_cast<DownloadSource>(i);
421 const int kMaxMiBs = 10240; // Anything above 10GB goes in the last bucket.
422
Jay Srinivasandbd9ea22013-04-22 17:45:19 -0700423 metric = "Installer.SuccessfulMBsDownloadedFrom" + utils::ToString(source);
Jay Srinivasan19409b72013-04-12 19:23:36 -0700424 uint64_t mbs = GetCurrentBytesDownloaded(source) / kNumBytesInOneMiB;
Jay Srinivasandbd9ea22013-04-22 17:45:19 -0700425
426 // Count this download source as having been used if we downloaded any
427 // bytes that contributed to the final success of the update.
428 if (mbs)
429 download_sources_used |= (1 << source);
430
431 successful_mbs += mbs;
432 LOG(INFO) << "Uploading " << mbs << " (MBs) for metric " << metric;
Jay Srinivasan19409b72013-04-12 19:23:36 -0700433 system_state_->metrics_lib()->SendToUMA(metric,
434 mbs,
435 0, // min
436 kMaxMiBs,
437 kNumDefaultUmaBuckets);
438 SetCurrentBytesDownloaded(source, 0, true);
439
440 metric = "Installer.TotalMBsDownloadedFrom" + utils::ToString(source);
441 mbs = GetTotalBytesDownloaded(source) / kNumBytesInOneMiB;
Jay Srinivasandbd9ea22013-04-22 17:45:19 -0700442 total_mbs += mbs;
443 LOG(INFO) << "Uploading " << mbs << " (MBs) for metric " << metric;
Jay Srinivasan19409b72013-04-12 19:23:36 -0700444 system_state_->metrics_lib()->SendToUMA(metric,
445 mbs,
446 0, // min
447 kMaxMiBs,
448 kNumDefaultUmaBuckets);
449
450 SetTotalBytesDownloaded(source, 0, true);
451 }
Jay Srinivasandbd9ea22013-04-22 17:45:19 -0700452
453 metric = "Installer.DownloadSourcesUsed";
454 LOG(INFO) << "Uploading 0x" << std::hex << download_sources_used
455 << " (bit flags) for metric " << metric;
456 int num_buckets = std::min(1 << kNumDownloadSources, kNumDefaultUmaBuckets);
457 system_state_->metrics_lib()->SendToUMA(metric,
458 download_sources_used,
459 0, // min
460 1 << kNumDownloadSources,
461 num_buckets);
462
463 if (successful_mbs) {
464 metric = "Installer.DownloadOverheadPercentage";
465 int percent_overhead = (total_mbs - successful_mbs) * 100 / successful_mbs;
466 LOG(INFO) << "Uploading " << percent_overhead << "% for metric " << metric;
467 system_state_->metrics_lib()->SendToUMA(metric,
468 percent_overhead,
469 0, // min: 0% overhead
470 1000, // max: 1000% overhead
471 kNumDefaultUmaBuckets);
472 }
Jay Srinivasan19409b72013-04-12 19:23:36 -0700473}
474
David Zeuthencc6f9962013-04-18 11:57:24 -0700475void PayloadState::ReportUpdateUrlSwitchesMetric() {
476 string metric = "Installer.UpdateURLSwitches";
477 int value = static_cast<int>(url_switch_count_);
478
479 LOG(INFO) << "Uploading " << value << " (count) for metric " << metric;
480 system_state_->metrics_lib()->SendToUMA(
481 metric,
482 value,
483 0, // min value
484 100, // max value
485 kNumDefaultUmaBuckets);
486}
487
Chris Sosabe45bef2013-04-09 18:25:12 -0700488void PayloadState::ReportRebootMetrics() {
489 // Report the number of num_reboots.
490 string metric = "Installer.UpdateNumReboots";
491 uint32_t num_reboots = GetNumReboots();
492 LOG(INFO) << "Uploading reboot count of " << num_reboots << " for metric "
493 << metric;
494 system_state_->metrics_lib()->SendToUMA(
495 metric,
496 static_cast<int>(num_reboots), // sample
497 0, // min = 0.
498 50, // max
499 25); // buckets
500 SetNumReboots(0);
501}
502
503void PayloadState::UpdateNumReboots() {
504 // We only update the reboot count when the system has been detected to have
505 // been rebooted.
506 if (!system_state_->system_rebooted()) {
507 return;
508 }
509
510 SetNumReboots(GetNumReboots() + 1);
511}
512
513void PayloadState::SetNumReboots(uint32_t num_reboots) {
514 CHECK(prefs_);
515 num_reboots_ = num_reboots;
516 prefs_->SetInt64(kPrefsNumReboots, num_reboots);
517 LOG(INFO) << "Number of Reboots during current update attempt = "
518 << num_reboots_;
519}
520
Jay Srinivasan08262882012-12-28 19:29:43 -0800521void PayloadState::ResetPersistedState() {
522 SetPayloadAttemptNumber(0);
523 SetUrlIndex(0);
524 SetUrlFailureCount(0);
David Zeuthencc6f9962013-04-18 11:57:24 -0700525 SetUrlSwitchCount(0);
Jay Srinivasan08262882012-12-28 19:29:43 -0800526 UpdateBackoffExpiryTime(); // This will reset the backoff expiry time.
David Zeuthenf413fe52013-04-22 14:04:39 -0700527 SetUpdateTimestampStart(system_state_->clock()->GetWallclockTime());
David Zeuthen9a017f22013-04-11 16:10:26 -0700528 SetUpdateTimestampEnd(Time()); // Set to null time
529 SetUpdateDurationUptime(TimeDelta::FromSeconds(0));
Jay Srinivasan19409b72013-04-12 19:23:36 -0700530 ResetDownloadSourcesOnNewUpdate();
531}
532
533void PayloadState::ResetDownloadSourcesOnNewUpdate() {
534 for (int i = 0; i < kNumDownloadSources; i++) {
535 DownloadSource source = static_cast<DownloadSource>(i);
536 SetCurrentBytesDownloaded(source, 0, true);
537 // Note: Not resetting the TotalBytesDownloaded as we want that metric
538 // to count the bytes downloaded across various update attempts until
539 // we have successfully applied the update.
540 }
541}
542
543int64_t PayloadState::GetPersistedValue(const string& key) {
544 CHECK(prefs_);
545 if (!prefs_->Exists(key))
546 return 0;
547
548 int64_t stored_value;
549 if (!prefs_->GetInt64(key, &stored_value))
550 return 0;
551
552 if (stored_value < 0) {
553 LOG(ERROR) << key << ": Invalid value (" << stored_value
554 << ") in persisted state. Defaulting to 0";
555 return 0;
556 }
557
558 return stored_value;
Jay Srinivasan08262882012-12-28 19:29:43 -0800559}
560
561string PayloadState::CalculateResponseSignature() {
562 string response_sign = StringPrintf("NumURLs = %d\n",
Jay Srinivasan53173b92013-05-17 17:13:01 -0700563 candidate_urls_.size());
Jay Srinivasan08262882012-12-28 19:29:43 -0800564
Jay Srinivasan53173b92013-05-17 17:13:01 -0700565 for (size_t i = 0; i < candidate_urls_.size(); i++)
566 response_sign += StringPrintf("Candidate Url%d = %s\n",
567 i, candidate_urls_[i].c_str());
Jay Srinivasan08262882012-12-28 19:29:43 -0800568
569 response_sign += StringPrintf("Payload Size = %llu\n"
570 "Payload Sha256 Hash = %s\n"
571 "Metadata Size = %llu\n"
572 "Metadata Signature = %s\n"
573 "Is Delta Payload = %d\n"
574 "Max Failure Count Per Url = %d\n"
575 "Disable Payload Backoff = %d\n",
576 response_.size,
577 response_.hash.c_str(),
578 response_.metadata_size,
579 response_.metadata_signature.c_str(),
580 response_.is_delta_payload,
581 response_.max_failure_count_per_url,
582 response_.disable_payload_backoff);
583 return response_sign;
584}
585
586void PayloadState::LoadResponseSignature() {
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800587 CHECK(prefs_);
588 string stored_value;
Jay Srinivasan08262882012-12-28 19:29:43 -0800589 if (prefs_->Exists(kPrefsCurrentResponseSignature) &&
590 prefs_->GetString(kPrefsCurrentResponseSignature, &stored_value)) {
591 SetResponseSignature(stored_value);
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800592 }
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800593}
594
Jay Srinivasan19409b72013-04-12 19:23:36 -0700595void PayloadState::SetResponseSignature(const string& response_signature) {
Jay Srinivasan08262882012-12-28 19:29:43 -0800596 CHECK(prefs_);
597 response_signature_ = response_signature;
598 LOG(INFO) << "Current Response Signature = \n" << response_signature_;
599 prefs_->SetString(kPrefsCurrentResponseSignature, response_signature_);
600}
601
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -0800602void PayloadState::LoadPayloadAttemptNumber() {
Jay Srinivasan19409b72013-04-12 19:23:36 -0700603 SetPayloadAttemptNumber(GetPersistedValue(kPrefsPayloadAttemptNumber));
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -0800604}
605
606void PayloadState::SetPayloadAttemptNumber(uint32_t payload_attempt_number) {
607 CHECK(prefs_);
608 payload_attempt_number_ = payload_attempt_number;
609 LOG(INFO) << "Payload Attempt Number = " << payload_attempt_number_;
610 prefs_->SetInt64(kPrefsPayloadAttemptNumber, payload_attempt_number_);
611}
612
613void PayloadState::LoadUrlIndex() {
Jay Srinivasan19409b72013-04-12 19:23:36 -0700614 SetUrlIndex(GetPersistedValue(kPrefsCurrentUrlIndex));
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800615}
616
617void PayloadState::SetUrlIndex(uint32_t url_index) {
618 CHECK(prefs_);
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800619 url_index_ = url_index;
620 LOG(INFO) << "Current URL Index = " << url_index_;
621 prefs_->SetInt64(kPrefsCurrentUrlIndex, url_index_);
Jay Srinivasan19409b72013-04-12 19:23:36 -0700622
623 // Also update the download source, which is purely dependent on the
624 // current URL index alone.
625 UpdateCurrentDownloadSource();
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -0800626}
627
David Zeuthencc6f9962013-04-18 11:57:24 -0700628void PayloadState::LoadUrlSwitchCount() {
629 SetUrlSwitchCount(GetPersistedValue(kPrefsUrlSwitchCount));
630}
631
632void PayloadState::SetUrlSwitchCount(uint32_t url_switch_count) {
633 CHECK(prefs_);
634 url_switch_count_ = url_switch_count;
635 LOG(INFO) << "URL Switch Count = " << url_switch_count_;
636 prefs_->SetInt64(kPrefsUrlSwitchCount, url_switch_count_);
637}
638
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -0800639void PayloadState::LoadUrlFailureCount() {
Jay Srinivasan19409b72013-04-12 19:23:36 -0700640 SetUrlFailureCount(GetPersistedValue(kPrefsCurrentUrlFailureCount));
Jay Srinivasan2b5a0f02012-12-19 17:25:56 -0800641}
642
643void PayloadState::SetUrlFailureCount(uint32_t url_failure_count) {
644 CHECK(prefs_);
645 url_failure_count_ = url_failure_count;
646 LOG(INFO) << "Current URL (Url" << GetUrlIndex()
647 << ")'s Failure Count = " << url_failure_count_;
648 prefs_->SetInt64(kPrefsCurrentUrlFailureCount, url_failure_count_);
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800649}
650
Jay Srinivasan08262882012-12-28 19:29:43 -0800651void PayloadState::LoadBackoffExpiryTime() {
652 CHECK(prefs_);
653 int64_t stored_value;
654 if (!prefs_->Exists(kPrefsBackoffExpiryTime))
655 return;
656
657 if (!prefs_->GetInt64(kPrefsBackoffExpiryTime, &stored_value))
658 return;
659
660 Time stored_time = Time::FromInternalValue(stored_value);
661 if (stored_time > Time::Now() + TimeDelta::FromDays(kMaxBackoffDays)) {
662 LOG(ERROR) << "Invalid backoff expiry time ("
663 << utils::ToString(stored_time)
664 << ") in persisted state. Resetting.";
665 stored_time = Time();
666 }
667 SetBackoffExpiryTime(stored_time);
668}
669
670void PayloadState::SetBackoffExpiryTime(const Time& new_time) {
671 CHECK(prefs_);
672 backoff_expiry_time_ = new_time;
673 LOG(INFO) << "Backoff Expiry Time = "
674 << utils::ToString(backoff_expiry_time_);
675 prefs_->SetInt64(kPrefsBackoffExpiryTime,
676 backoff_expiry_time_.ToInternalValue());
677}
678
David Zeuthen9a017f22013-04-11 16:10:26 -0700679TimeDelta PayloadState::GetUpdateDuration() {
David Zeuthenf413fe52013-04-22 14:04:39 -0700680 Time end_time = update_timestamp_end_.is_null()
681 ? system_state_->clock()->GetWallclockTime() :
682 update_timestamp_end_;
David Zeuthen9a017f22013-04-11 16:10:26 -0700683 return end_time - update_timestamp_start_;
684}
685
686void PayloadState::LoadUpdateTimestampStart() {
687 int64_t stored_value;
688 Time stored_time;
689
690 CHECK(prefs_);
691
David Zeuthenf413fe52013-04-22 14:04:39 -0700692 Time now = system_state_->clock()->GetWallclockTime();
David Zeuthen9a017f22013-04-11 16:10:26 -0700693
694 if (!prefs_->Exists(kPrefsUpdateTimestampStart)) {
695 // The preference missing is not unexpected - in that case, just
696 // use the current time as start time
697 stored_time = now;
698 } else if (!prefs_->GetInt64(kPrefsUpdateTimestampStart, &stored_value)) {
699 LOG(ERROR) << "Invalid UpdateTimestampStart value. Resetting.";
700 stored_time = now;
701 } else {
702 stored_time = Time::FromInternalValue(stored_value);
703 }
704
705 // Sanity check: If the time read from disk is in the future
706 // (modulo some slack to account for possible NTP drift
707 // adjustments), something is fishy and we should report and
708 // reset.
709 TimeDelta duration_according_to_stored_time = now - stored_time;
710 if (duration_according_to_stored_time < -kDurationSlack) {
711 LOG(ERROR) << "The UpdateTimestampStart value ("
712 << utils::ToString(stored_time)
713 << ") in persisted state is "
David Zeuthen674c3182013-04-18 14:05:20 -0700714 << utils::FormatTimeDelta(duration_according_to_stored_time)
715 << " in the future. Resetting.";
David Zeuthen9a017f22013-04-11 16:10:26 -0700716 stored_time = now;
717 }
718
719 SetUpdateTimestampStart(stored_time);
720}
721
722void PayloadState::SetUpdateTimestampStart(const Time& value) {
723 CHECK(prefs_);
724 update_timestamp_start_ = value;
725 prefs_->SetInt64(kPrefsUpdateTimestampStart,
726 update_timestamp_start_.ToInternalValue());
727 LOG(INFO) << "Update Timestamp Start = "
728 << utils::ToString(update_timestamp_start_);
729}
730
731void PayloadState::SetUpdateTimestampEnd(const Time& value) {
732 update_timestamp_end_ = value;
733 LOG(INFO) << "Update Timestamp End = "
734 << utils::ToString(update_timestamp_end_);
735}
736
737TimeDelta PayloadState::GetUpdateDurationUptime() {
738 return update_duration_uptime_;
739}
740
741void PayloadState::LoadUpdateDurationUptime() {
742 int64_t stored_value;
743 TimeDelta stored_delta;
744
745 CHECK(prefs_);
746
747 if (!prefs_->Exists(kPrefsUpdateDurationUptime)) {
748 // The preference missing is not unexpected - in that case, just
749 // we'll use zero as the delta
750 } else if (!prefs_->GetInt64(kPrefsUpdateDurationUptime, &stored_value)) {
751 LOG(ERROR) << "Invalid UpdateDurationUptime value. Resetting.";
752 stored_delta = TimeDelta::FromSeconds(0);
753 } else {
754 stored_delta = TimeDelta::FromInternalValue(stored_value);
755 }
756
757 // Sanity-check: Uptime can never be greater than the wall-clock
758 // difference (modulo some slack). If it is, report and reset
759 // to the wall-clock difference.
760 TimeDelta diff = GetUpdateDuration() - stored_delta;
761 if (diff < -kDurationSlack) {
762 LOG(ERROR) << "The UpdateDurationUptime value ("
David Zeuthen674c3182013-04-18 14:05:20 -0700763 << utils::FormatTimeDelta(stored_delta)
David Zeuthen9a017f22013-04-11 16:10:26 -0700764 << ") in persisted state is "
David Zeuthen674c3182013-04-18 14:05:20 -0700765 << utils::FormatTimeDelta(diff)
766 << " larger than the wall-clock delta. Resetting.";
David Zeuthen9a017f22013-04-11 16:10:26 -0700767 stored_delta = update_duration_current_;
768 }
769
770 SetUpdateDurationUptime(stored_delta);
771}
772
Chris Sosabe45bef2013-04-09 18:25:12 -0700773void PayloadState::LoadNumReboots() {
774 SetNumReboots(GetPersistedValue(kPrefsNumReboots));
775}
776
David Zeuthen9a017f22013-04-11 16:10:26 -0700777void PayloadState::SetUpdateDurationUptimeExtended(const TimeDelta& value,
778 const Time& timestamp,
779 bool use_logging) {
780 CHECK(prefs_);
781 update_duration_uptime_ = value;
782 update_duration_uptime_timestamp_ = timestamp;
783 prefs_->SetInt64(kPrefsUpdateDurationUptime,
784 update_duration_uptime_.ToInternalValue());
785 if (use_logging) {
786 LOG(INFO) << "Update Duration Uptime = "
David Zeuthen674c3182013-04-18 14:05:20 -0700787 << utils::FormatTimeDelta(update_duration_uptime_);
David Zeuthen9a017f22013-04-11 16:10:26 -0700788 }
789}
790
791void PayloadState::SetUpdateDurationUptime(const TimeDelta& value) {
David Zeuthenf413fe52013-04-22 14:04:39 -0700792 Time now = system_state_->clock()->GetMonotonicTime();
793 SetUpdateDurationUptimeExtended(value, now, true);
David Zeuthen9a017f22013-04-11 16:10:26 -0700794}
795
796void PayloadState::CalculateUpdateDurationUptime() {
David Zeuthenf413fe52013-04-22 14:04:39 -0700797 Time now = system_state_->clock()->GetMonotonicTime();
David Zeuthen9a017f22013-04-11 16:10:26 -0700798 TimeDelta uptime_since_last_update = now - update_duration_uptime_timestamp_;
799 TimeDelta new_uptime = update_duration_uptime_ + uptime_since_last_update;
800 // We're frequently called so avoid logging this write
801 SetUpdateDurationUptimeExtended(new_uptime, now, false);
802}
803
David Zeuthen674c3182013-04-18 14:05:20 -0700804void PayloadState::ReportDurationMetrics() {
805 TimeDelta duration = GetUpdateDuration();
806 TimeDelta duration_uptime = GetUpdateDurationUptime();
807 string metric;
808
809 metric = "Installer.UpdateDurationMinutes";
810 system_state_->metrics_lib()->SendToUMA(
811 metric,
812 static_cast<int>(duration.InMinutes()),
813 1, // min: 1 minute
814 365*24*60, // max: 1 year (approx)
815 kNumDefaultUmaBuckets);
816 LOG(INFO) << "Uploading " << utils::FormatTimeDelta(duration)
817 << " for metric " << metric;
818
819 metric = "Installer.UpdateDurationUptimeMinutes";
820 system_state_->metrics_lib()->SendToUMA(
821 metric,
822 static_cast<int>(duration_uptime.InMinutes()),
823 1, // min: 1 minute
824 30*24*60, // max: 1 month (approx)
825 kNumDefaultUmaBuckets);
826 LOG(INFO) << "Uploading " << utils::FormatTimeDelta(duration_uptime)
827 << " for metric " << metric;
828
829 prefs_->Delete(kPrefsUpdateTimestampStart);
830 prefs_->Delete(kPrefsUpdateDurationUptime);
831}
832
Jay Srinivasan19409b72013-04-12 19:23:36 -0700833string PayloadState::GetPrefsKey(const string& prefix, DownloadSource source) {
834 return prefix + "-from-" + utils::ToString(source);
835}
836
837void PayloadState::LoadCurrentBytesDownloaded(DownloadSource source) {
838 string key = GetPrefsKey(kPrefsCurrentBytesDownloaded, source);
839 SetCurrentBytesDownloaded(source, GetPersistedValue(key), true);
840}
841
842void PayloadState::SetCurrentBytesDownloaded(
843 DownloadSource source,
844 uint64_t current_bytes_downloaded,
845 bool log) {
846 CHECK(prefs_);
847
848 if (source >= kNumDownloadSources)
849 return;
850
851 // Update the in-memory value.
852 current_bytes_downloaded_[source] = current_bytes_downloaded;
853
854 string prefs_key = GetPrefsKey(kPrefsCurrentBytesDownloaded, source);
855 prefs_->SetInt64(prefs_key, current_bytes_downloaded);
856 LOG_IF(INFO, log) << "Current bytes downloaded for "
857 << utils::ToString(source) << " = "
858 << GetCurrentBytesDownloaded(source);
859}
860
861void PayloadState::LoadTotalBytesDownloaded(DownloadSource source) {
862 string key = GetPrefsKey(kPrefsTotalBytesDownloaded, source);
863 SetTotalBytesDownloaded(source, GetPersistedValue(key), true);
864}
865
866void PayloadState::SetTotalBytesDownloaded(
867 DownloadSource source,
868 uint64_t total_bytes_downloaded,
869 bool log) {
870 CHECK(prefs_);
871
872 if (source >= kNumDownloadSources)
873 return;
874
875 // Update the in-memory value.
876 total_bytes_downloaded_[source] = total_bytes_downloaded;
877
878 // Persist.
879 string prefs_key = GetPrefsKey(kPrefsTotalBytesDownloaded, source);
880 prefs_->SetInt64(prefs_key, total_bytes_downloaded);
881 LOG_IF(INFO, log) << "Total bytes downloaded for "
882 << utils::ToString(source) << " = "
883 << GetTotalBytesDownloaded(source);
884}
885
Jay Srinivasan53173b92013-05-17 17:13:01 -0700886void PayloadState::ComputeCandidateUrls() {
887 bool http_url_ok = false;
888
889 if (system_state_->IsOfficialBuild()) {
890 const policy::DevicePolicy* policy = system_state_->device_policy();
891 if (!(policy && policy->GetHttpDownloadsEnabled(&http_url_ok) &&
892 http_url_ok))
893 LOG(INFO) << "Downloads via HTTP Url are not enabled by device policy";
894 } else {
895 LOG(INFO) << "Allowing HTTP downloads for unofficial builds";
896 http_url_ok = true;
897 }
898
899 candidate_urls_.clear();
900 for (size_t i = 0; i < response_.payload_urls.size(); i++) {
901 string candidate_url = response_.payload_urls[i];
902 if (StartsWithASCII(candidate_url, "http://", false) && !http_url_ok)
903 continue;
904 candidate_urls_.push_back(candidate_url);
905 LOG(INFO) << "Candidate Url" << (candidate_urls_.size() - 1)
906 << ": " << candidate_url;
907 }
908
909 LOG(INFO) << "Found " << candidate_urls_.size() << " candidate URLs "
910 << "out of " << response_.payload_urls.size() << " URLs supplied";
911}
912
Jay Srinivasan6f6ea002012-12-14 11:26:28 -0800913} // namespace chromeos_update_engine