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 
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       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 
147 metrics::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 
265 metrics::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 
297 int64_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 
315 void 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 
322 void 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 
329 void 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 
338 void 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 
347 void 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 
356 bool 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