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