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