1//
2// Copyright (C) 2015 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/metrics_utils.h"
18
19#include <string>
20
21#include <base/time/time.h>
22
23#include "update_engine/common/clock_interface.h"
24#include "update_engine/common/constants.h"
25#include "update_engine/common/utils.h"
26
27using base::Time;
28using base::TimeDelta;
29
30namespace chromeos_update_engine {
31namespace metrics_utils {
32
33metrics::AttemptResult GetAttemptResult(ErrorCode code) {
34  ErrorCode base_code = static_cast<ErrorCode>(
35      static_cast<int>(code) & ~static_cast<int>(ErrorCode::kSpecialFlags));
36
37  switch (base_code) {
38    case ErrorCode::kSuccess:
39      return metrics::AttemptResult::kUpdateSucceeded;
40
41    case ErrorCode::kUpdatedButNotActive:
42      return metrics::AttemptResult::kUpdateSucceededNotActive;
43
44    case ErrorCode::kDownloadTransferError:
45    case ErrorCode::kInternalLibCurlError:
46    case ErrorCode::kUnresolvedHostError:
47    case ErrorCode::kUnresolvedHostRecovered:
48      return metrics::AttemptResult::kPayloadDownloadError;
49
50    case ErrorCode::kDownloadInvalidMetadataSize:
51    case ErrorCode::kDownloadInvalidMetadataMagicString:
52    case ErrorCode::kDownloadMetadataSignatureError:
53    case ErrorCode::kDownloadMetadataSignatureVerificationError:
54    case ErrorCode::kPayloadMismatchedType:
55    case ErrorCode::kUnsupportedMajorPayloadVersion:
56    case ErrorCode::kUnsupportedMinorPayloadVersion:
57    case ErrorCode::kDownloadNewPartitionInfoError:
58    case ErrorCode::kDownloadSignatureMissingInManifest:
59    case ErrorCode::kDownloadManifestParseError:
60    case ErrorCode::kDownloadOperationHashMissingError:
61      return metrics::AttemptResult::kMetadataMalformed;
62
63    case ErrorCode::kDownloadOperationHashMismatch:
64    case ErrorCode::kDownloadOperationHashVerificationError:
65      return metrics::AttemptResult::kOperationMalformed;
66
67    case ErrorCode::kDownloadOperationExecutionError:
68    case ErrorCode::kInstallDeviceOpenError:
69    case ErrorCode::kKernelDeviceOpenError:
70    case ErrorCode::kDownloadWriteError:
71    case ErrorCode::kFilesystemCopierError:
72    case ErrorCode::kFilesystemVerifierError:
73    case ErrorCode::kVerityCalculationError:
74    case ErrorCode::kNotEnoughSpace:
75    case ErrorCode::kDeviceCorrupted:
76      return metrics::AttemptResult::kOperationExecutionError;
77
78    case ErrorCode::kDownloadMetadataSignatureMismatch:
79      return metrics::AttemptResult::kMetadataVerificationFailed;
80
81    case ErrorCode::kPayloadSizeMismatchError:
82    case ErrorCode::kPayloadHashMismatchError:
83    case ErrorCode::kDownloadPayloadVerificationError:
84    case ErrorCode::kSignedDeltaPayloadExpectedError:
85    case ErrorCode::kDownloadPayloadPubKeyVerificationError:
86    case ErrorCode::kPayloadTimestampError:
87      return metrics::AttemptResult::kPayloadVerificationFailed;
88
89    case ErrorCode::kNewRootfsVerificationError:
90    case ErrorCode::kNewKernelVerificationError:
91    case ErrorCode::kRollbackNotPossible:
92      return metrics::AttemptResult::kVerificationFailed;
93
94    case ErrorCode::kPostinstallRunnerError:
95    case ErrorCode::kPostinstallBootedFromFirmwareB:
96    case ErrorCode::kPostinstallFirmwareRONotUpdatable:
97    case ErrorCode::kPostInstallMountError:
98      return metrics::AttemptResult::kPostInstallFailed;
99
100    case ErrorCode::kUserCanceled:
101      return metrics::AttemptResult::kUpdateCanceled;
102
103    // We should never get these errors in the update-attempt stage so
104    // return internal error if this happens.
105    case ErrorCode::kError:
106    case ErrorCode::kOmahaRequestXMLParseError:
107    case ErrorCode::kOmahaRequestError:
108    case ErrorCode::kOmahaResponseHandlerError:
109    case ErrorCode::kDownloadStateInitializationError:
110    case ErrorCode::kOmahaRequestEmptyResponseError:
111    case ErrorCode::kDownloadInvalidMetadataSignature:
112    case ErrorCode::kOmahaResponseInvalid:
113    case ErrorCode::kOmahaUpdateIgnoredPerPolicy:
114    case ErrorCode::kOmahaErrorInHTTPResponse:
115    case ErrorCode::kDownloadMetadataSignatureMissingError:
116    case ErrorCode::kOmahaUpdateDeferredForBackoff:
117    case ErrorCode::kPostinstallPowerwashError:
118    case ErrorCode::kUpdateCanceledByChannelChange:
119    case ErrorCode::kOmahaRequestXMLHasEntityDecl:
120    case ErrorCode::kOmahaUpdateIgnoredOverCellular:
121    case ErrorCode::kNoUpdate:
122    case ErrorCode::kFirstActiveOmahaPingSentPersistenceError:
123    case ErrorCode::kPackageExcludedFromUpdate:
124      return metrics::AttemptResult::kInternalError;
125
126    case ErrorCode::kOmahaUpdateDeferredPerPolicy:
127    case ErrorCode::kNonCriticalUpdateInOOBE:
128      return metrics::AttemptResult::kUpdateSkipped;
129
130    // Special flags. These can't happen (we mask them out above) but
131    // the compiler doesn't know that. Just break out so we can warn and
132    // return |kInternalError|.
133    case ErrorCode::kUmaReportedMax:
134    case ErrorCode::kOmahaRequestHTTPResponseBase:
135    case ErrorCode::kDevModeFlag:
136    case ErrorCode::kResumedFlag:
137    case ErrorCode::kTestImageFlag:
138    case ErrorCode::kTestOmahaUrlFlag:
139    case ErrorCode::kSpecialFlags:
140      break;
141  }
142
143  LOG(ERROR) << "Unexpected error code " << base_code;
144  return metrics::AttemptResult::kInternalError;
145}
146
147metrics::DownloadErrorCode GetDownloadErrorCode(ErrorCode code) {
148  ErrorCode base_code = static_cast<ErrorCode>(
149      static_cast<int>(code) & ~static_cast<int>(ErrorCode::kSpecialFlags));
150
151  if (base_code >= ErrorCode::kOmahaRequestHTTPResponseBase) {
152    int http_status =
153        static_cast<int>(base_code) -
154        static_cast<int>(ErrorCode::kOmahaRequestHTTPResponseBase);
155    if (http_status >= 200 && http_status <= 599) {
156      return static_cast<metrics::DownloadErrorCode>(
157          static_cast<int>(metrics::DownloadErrorCode::kHttpStatus200) +
158          http_status - 200);
159    } else if (http_status == 0) {
160      // The code is using HTTP Status 0 for "Unable to get http
161      // response code."
162      return metrics::DownloadErrorCode::kDownloadError;
163    }
164    LOG(WARNING) << "Unexpected HTTP status code " << http_status;
165    return metrics::DownloadErrorCode::kHttpStatusOther;
166  }
167
168  switch (base_code) {
169    // Unfortunately, ErrorCode::kDownloadTransferError is returned for a wide
170    // variety of errors (proxy errors, host not reachable, timeouts etc.).
171    //
172    // For now just map that to kDownloading. See http://crbug.com/355745
173    // for how we plan to add more detail in the future.
174    case ErrorCode::kDownloadTransferError:
175      return metrics::DownloadErrorCode::kDownloadError;
176
177    case ErrorCode::kInternalLibCurlError:
178      return metrics::DownloadErrorCode::kInternalLibCurlError;
179    case ErrorCode::kUnresolvedHostError:
180      return metrics::DownloadErrorCode::kUnresolvedHostError;
181    case ErrorCode::kUnresolvedHostRecovered:
182      return metrics::DownloadErrorCode::kUnresolvedHostRecovered;
183
184    // All of these error codes are not related to downloading so break
185    // out so we can warn and return InputMalformed.
186    case ErrorCode::kSuccess:
187    case ErrorCode::kError:
188    case ErrorCode::kOmahaRequestError:
189    case ErrorCode::kOmahaResponseHandlerError:
190    case ErrorCode::kFilesystemCopierError:
191    case ErrorCode::kPostinstallRunnerError:
192    case ErrorCode::kPostInstallMountError:
193    case ErrorCode::kPayloadMismatchedType:
194    case ErrorCode::kInstallDeviceOpenError:
195    case ErrorCode::kKernelDeviceOpenError:
196    case ErrorCode::kPayloadHashMismatchError:
197    case ErrorCode::kPayloadSizeMismatchError:
198    case ErrorCode::kDownloadPayloadVerificationError:
199    case ErrorCode::kDownloadNewPartitionInfoError:
200    case ErrorCode::kDownloadWriteError:
201    case ErrorCode::kNewRootfsVerificationError:
202    case ErrorCode::kNewKernelVerificationError:
203    case ErrorCode::kSignedDeltaPayloadExpectedError:
204    case ErrorCode::kDownloadPayloadPubKeyVerificationError:
205    case ErrorCode::kPostinstallBootedFromFirmwareB:
206    case ErrorCode::kDownloadStateInitializationError:
207    case ErrorCode::kDownloadInvalidMetadataMagicString:
208    case ErrorCode::kDownloadSignatureMissingInManifest:
209    case ErrorCode::kDownloadManifestParseError:
210    case ErrorCode::kDownloadMetadataSignatureError:
211    case ErrorCode::kDownloadMetadataSignatureVerificationError:
212    case ErrorCode::kDownloadMetadataSignatureMismatch:
213    case ErrorCode::kDownloadOperationHashVerificationError:
214    case ErrorCode::kDownloadOperationExecutionError:
215    case ErrorCode::kDownloadOperationHashMismatch:
216    case ErrorCode::kOmahaRequestEmptyResponseError:
217    case ErrorCode::kOmahaRequestXMLParseError:
218    case ErrorCode::kDownloadInvalidMetadataSize:
219    case ErrorCode::kDownloadInvalidMetadataSignature:
220    case ErrorCode::kOmahaResponseInvalid:
221    case ErrorCode::kOmahaUpdateIgnoredPerPolicy:
222    case ErrorCode::kOmahaUpdateDeferredPerPolicy:
223    case ErrorCode::kNonCriticalUpdateInOOBE:
224    case ErrorCode::kOmahaErrorInHTTPResponse:
225    case ErrorCode::kDownloadOperationHashMissingError:
226    case ErrorCode::kDownloadMetadataSignatureMissingError:
227    case ErrorCode::kOmahaUpdateDeferredForBackoff:
228    case ErrorCode::kPostinstallPowerwashError:
229    case ErrorCode::kUpdateCanceledByChannelChange:
230    case ErrorCode::kPostinstallFirmwareRONotUpdatable:
231    case ErrorCode::kUnsupportedMajorPayloadVersion:
232    case ErrorCode::kUnsupportedMinorPayloadVersion:
233    case ErrorCode::kOmahaRequestXMLHasEntityDecl:
234    case ErrorCode::kFilesystemVerifierError:
235    case ErrorCode::kUserCanceled:
236    case ErrorCode::kOmahaUpdateIgnoredOverCellular:
237    case ErrorCode::kPayloadTimestampError:
238    case ErrorCode::kUpdatedButNotActive:
239    case ErrorCode::kNoUpdate:
240    case ErrorCode::kRollbackNotPossible:
241    case ErrorCode::kFirstActiveOmahaPingSentPersistenceError:
242    case ErrorCode::kVerityCalculationError:
243    case ErrorCode::kNotEnoughSpace:
244    case ErrorCode::kDeviceCorrupted:
245    case ErrorCode::kPackageExcludedFromUpdate:
246      break;
247
248    // Special flags. These can't happen (we mask them out above) but
249    // the compiler doesn't know that. Just break out so we can warn and
250    // return |kInputMalformed|.
251    case ErrorCode::kUmaReportedMax:
252    case ErrorCode::kOmahaRequestHTTPResponseBase:
253    case ErrorCode::kDevModeFlag:
254    case ErrorCode::kResumedFlag:
255    case ErrorCode::kTestImageFlag:
256    case ErrorCode::kTestOmahaUrlFlag:
257    case ErrorCode::kSpecialFlags:
258      LOG(ERROR) << "Unexpected error code " << base_code;
259      break;
260  }
261
262  return metrics::DownloadErrorCode::kInputMalformed;
263}
264
265metrics::ConnectionType GetConnectionType(ConnectionType type,
266                                          ConnectionTethering tethering) {
267  switch (type) {
268    case ConnectionType::kUnknown:
269      return metrics::ConnectionType::kUnknown;
270
271    case ConnectionType::kDisconnected:
272      return metrics::ConnectionType::kDisconnected;
273
274    case ConnectionType::kEthernet:
275      if (tethering == ConnectionTethering::kConfirmed)
276        return metrics::ConnectionType::kTetheredEthernet;
277      else
278        return metrics::ConnectionType::kEthernet;
279
280    case ConnectionType::kWifi:
281      if (tethering == ConnectionTethering::kConfirmed)
282        return metrics::ConnectionType::kTetheredWifi;
283      else
284        return metrics::ConnectionType::kWifi;
285
286    case ConnectionType::kCellular:
287      return metrics::ConnectionType::kCellular;
288  }
289
290  LOG(ERROR) << "Unexpected network connection type: type="
291             << static_cast<int>(type)
292             << ", tethering=" << static_cast<int>(tethering);
293
294  return metrics::ConnectionType::kUnknown;
295}
296
297int64_t GetPersistedValue(const std::string& key, PrefsInterface* prefs) {
298  CHECK(prefs);
299  if (!prefs->Exists(key))
300    return 0;
301
302  int64_t stored_value;
303  if (!prefs->GetInt64(key, &stored_value))
304    return 0;
305
306  if (stored_value < 0) {
307    LOG(ERROR) << key << ": Invalid value (" << stored_value
308               << ") in persisted state. Defaulting to 0";
309    return 0;
310  }
311
312  return stored_value;
313}
314
315void SetNumReboots(int64_t num_reboots, PrefsInterface* prefs) {
316  CHECK(prefs);
317  prefs->SetInt64(kPrefsNumReboots, num_reboots);
318  LOG(INFO) << "Number of Reboots during current update attempt = "
319            << num_reboots;
320}
321
322void SetPayloadAttemptNumber(int64_t payload_attempt_number,
323                             PrefsInterface* prefs) {
324  CHECK(prefs);
325  prefs->SetInt64(kPrefsPayloadAttemptNumber, payload_attempt_number);
326  LOG(INFO) << "Payload Attempt Number = " << payload_attempt_number;
327}
328
329void SetSystemUpdatedMarker(ClockInterface* clock, PrefsInterface* prefs) {
330  CHECK(prefs);
331  CHECK(clock);
332  Time update_finish_time = clock->GetMonotonicTime();
333  prefs->SetInt64(kPrefsSystemUpdatedMarker,
334                  update_finish_time.ToInternalValue());
335  LOG(INFO) << "Updated Marker = " << utils::ToString(update_finish_time);
336}
337
338void SetUpdateTimestampStart(const Time& update_start_time,
339                             PrefsInterface* prefs) {
340  CHECK(prefs);
341  prefs->SetInt64(kPrefsUpdateTimestampStart,
342                  update_start_time.ToInternalValue());
343  LOG(INFO) << "Update Monotonic Timestamp Start = "
344            << utils::ToString(update_start_time);
345}
346
347void SetUpdateBootTimestampStart(const base::Time& update_start_boot_time,
348                                 PrefsInterface* prefs) {
349  CHECK(prefs);
350  prefs->SetInt64(kPrefsUpdateBootTimestampStart,
351                  update_start_boot_time.ToInternalValue());
352  LOG(INFO) << "Update Boot Timestamp Start = "
353            << utils::ToString(update_start_boot_time);
354}
355
356bool LoadAndReportTimeToReboot(MetricsReporterInterface* metrics_reporter,
357                               PrefsInterface* prefs,
358                               ClockInterface* clock) {
359  CHECK(prefs);
360  CHECK(clock);
361  int64_t stored_value = GetPersistedValue(kPrefsSystemUpdatedMarker, prefs);
362  if (stored_value == 0)
363    return false;
364
365  Time system_updated_at = Time::FromInternalValue(stored_value);
366  TimeDelta time_to_reboot = clock->GetMonotonicTime() - system_updated_at;
367  if (time_to_reboot.ToInternalValue() < 0) {
368    LOG(ERROR) << "time_to_reboot is negative - system_updated_at: "
369               << utils::ToString(system_updated_at);
370    return false;
371  }
372  metrics_reporter->ReportTimeToReboot(time_to_reboot.InMinutes());
373  return true;
374}
375
376}  // namespace metrics_utils
377}  // namespace chromeos_update_engine
378