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