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/prefs_interface.h"
25 #include "update_engine/system_state.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::kDownloadTransferError:
42       return metrics::AttemptResult::kPayloadDownloadError;
43 
44     case ErrorCode::kDownloadInvalidMetadataSize:
45     case ErrorCode::kDownloadInvalidMetadataMagicString:
46     case ErrorCode::kDownloadMetadataSignatureError:
47     case ErrorCode::kDownloadMetadataSignatureVerificationError:
48     case ErrorCode::kPayloadMismatchedType:
49     case ErrorCode::kUnsupportedMajorPayloadVersion:
50     case ErrorCode::kUnsupportedMinorPayloadVersion:
51     case ErrorCode::kDownloadNewPartitionInfoError:
52     case ErrorCode::kDownloadSignatureMissingInManifest:
53     case ErrorCode::kDownloadManifestParseError:
54     case ErrorCode::kDownloadOperationHashMissingError:
55       return metrics::AttemptResult::kMetadataMalformed;
56 
57     case ErrorCode::kDownloadOperationHashMismatch:
58     case ErrorCode::kDownloadOperationHashVerificationError:
59       return metrics::AttemptResult::kOperationMalformed;
60 
61     case ErrorCode::kDownloadOperationExecutionError:
62     case ErrorCode::kInstallDeviceOpenError:
63     case ErrorCode::kKernelDeviceOpenError:
64     case ErrorCode::kDownloadWriteError:
65     case ErrorCode::kFilesystemCopierError:
66     case ErrorCode::kFilesystemVerifierError:
67       return metrics::AttemptResult::kOperationExecutionError;
68 
69     case ErrorCode::kDownloadMetadataSignatureMismatch:
70       return metrics::AttemptResult::kMetadataVerificationFailed;
71 
72     case ErrorCode::kPayloadSizeMismatchError:
73     case ErrorCode::kPayloadHashMismatchError:
74     case ErrorCode::kDownloadPayloadVerificationError:
75     case ErrorCode::kSignedDeltaPayloadExpectedError:
76     case ErrorCode::kDownloadPayloadPubKeyVerificationError:
77       return metrics::AttemptResult::kPayloadVerificationFailed;
78 
79     case ErrorCode::kNewRootfsVerificationError:
80     case ErrorCode::kNewKernelVerificationError:
81       return metrics::AttemptResult::kVerificationFailed;
82 
83     case ErrorCode::kPostinstallRunnerError:
84     case ErrorCode::kPostinstallBootedFromFirmwareB:
85     case ErrorCode::kPostinstallFirmwareRONotUpdatable:
86       return metrics::AttemptResult::kPostInstallFailed;
87 
88     case ErrorCode::kUserCanceled:
89       return metrics::AttemptResult::kUpdateCanceled;
90 
91     // We should never get these errors in the update-attempt stage so
92     // return internal error if this happens.
93     case ErrorCode::kError:
94     case ErrorCode::kOmahaRequestXMLParseError:
95     case ErrorCode::kOmahaRequestError:
96     case ErrorCode::kOmahaResponseHandlerError:
97     case ErrorCode::kDownloadStateInitializationError:
98     case ErrorCode::kOmahaRequestEmptyResponseError:
99     case ErrorCode::kDownloadInvalidMetadataSignature:
100     case ErrorCode::kOmahaResponseInvalid:
101     case ErrorCode::kOmahaUpdateIgnoredPerPolicy:
102     case ErrorCode::kOmahaUpdateDeferredPerPolicy:
103     case ErrorCode::kOmahaErrorInHTTPResponse:
104     case ErrorCode::kDownloadMetadataSignatureMissingError:
105     case ErrorCode::kOmahaUpdateDeferredForBackoff:
106     case ErrorCode::kPostinstallPowerwashError:
107     case ErrorCode::kUpdateCanceledByChannelChange:
108     case ErrorCode::kOmahaRequestXMLHasEntityDecl:
109       return metrics::AttemptResult::kInternalError;
110 
111     // Special flags. These can't happen (we mask them out above) but
112     // the compiler doesn't know that. Just break out so we can warn and
113     // return |kInternalError|.
114     case ErrorCode::kUmaReportedMax:
115     case ErrorCode::kOmahaRequestHTTPResponseBase:
116     case ErrorCode::kDevModeFlag:
117     case ErrorCode::kResumedFlag:
118     case ErrorCode::kTestImageFlag:
119     case ErrorCode::kTestOmahaUrlFlag:
120     case ErrorCode::kSpecialFlags:
121       break;
122   }
123 
124   LOG(ERROR) << "Unexpected error code " << base_code;
125   return metrics::AttemptResult::kInternalError;
126 }
127 
GetDownloadErrorCode(ErrorCode code)128 metrics::DownloadErrorCode GetDownloadErrorCode(ErrorCode code) {
129   ErrorCode base_code = static_cast<ErrorCode>(
130       static_cast<int>(code) & ~static_cast<int>(ErrorCode::kSpecialFlags));
131 
132   if (base_code >= ErrorCode::kOmahaRequestHTTPResponseBase) {
133     int http_status =
134         static_cast<int>(base_code) -
135         static_cast<int>(ErrorCode::kOmahaRequestHTTPResponseBase);
136     if (http_status >= 200 && http_status <= 599) {
137       return static_cast<metrics::DownloadErrorCode>(
138           static_cast<int>(metrics::DownloadErrorCode::kHttpStatus200) +
139           http_status - 200);
140     } else if (http_status == 0) {
141       // The code is using HTTP Status 0 for "Unable to get http
142       // response code."
143       return metrics::DownloadErrorCode::kDownloadError;
144     }
145     LOG(WARNING) << "Unexpected HTTP status code " << http_status;
146     return metrics::DownloadErrorCode::kHttpStatusOther;
147   }
148 
149   switch (base_code) {
150     // Unfortunately, ErrorCode::kDownloadTransferError is returned for a wide
151     // variety of errors (proxy errors, host not reachable, timeouts etc.).
152     //
153     // For now just map that to kDownloading. See http://crbug.com/355745
154     // for how we plan to add more detail in the future.
155     case ErrorCode::kDownloadTransferError:
156       return metrics::DownloadErrorCode::kDownloadError;
157 
158     // All of these error codes are not related to downloading so break
159     // out so we can warn and return InputMalformed.
160     case ErrorCode::kSuccess:
161     case ErrorCode::kError:
162     case ErrorCode::kOmahaRequestError:
163     case ErrorCode::kOmahaResponseHandlerError:
164     case ErrorCode::kFilesystemCopierError:
165     case ErrorCode::kPostinstallRunnerError:
166     case ErrorCode::kPayloadMismatchedType:
167     case ErrorCode::kInstallDeviceOpenError:
168     case ErrorCode::kKernelDeviceOpenError:
169     case ErrorCode::kPayloadHashMismatchError:
170     case ErrorCode::kPayloadSizeMismatchError:
171     case ErrorCode::kDownloadPayloadVerificationError:
172     case ErrorCode::kDownloadNewPartitionInfoError:
173     case ErrorCode::kDownloadWriteError:
174     case ErrorCode::kNewRootfsVerificationError:
175     case ErrorCode::kNewKernelVerificationError:
176     case ErrorCode::kSignedDeltaPayloadExpectedError:
177     case ErrorCode::kDownloadPayloadPubKeyVerificationError:
178     case ErrorCode::kPostinstallBootedFromFirmwareB:
179     case ErrorCode::kDownloadStateInitializationError:
180     case ErrorCode::kDownloadInvalidMetadataMagicString:
181     case ErrorCode::kDownloadSignatureMissingInManifest:
182     case ErrorCode::kDownloadManifestParseError:
183     case ErrorCode::kDownloadMetadataSignatureError:
184     case ErrorCode::kDownloadMetadataSignatureVerificationError:
185     case ErrorCode::kDownloadMetadataSignatureMismatch:
186     case ErrorCode::kDownloadOperationHashVerificationError:
187     case ErrorCode::kDownloadOperationExecutionError:
188     case ErrorCode::kDownloadOperationHashMismatch:
189     case ErrorCode::kOmahaRequestEmptyResponseError:
190     case ErrorCode::kOmahaRequestXMLParseError:
191     case ErrorCode::kDownloadInvalidMetadataSize:
192     case ErrorCode::kDownloadInvalidMetadataSignature:
193     case ErrorCode::kOmahaResponseInvalid:
194     case ErrorCode::kOmahaUpdateIgnoredPerPolicy:
195     case ErrorCode::kOmahaUpdateDeferredPerPolicy:
196     case ErrorCode::kOmahaErrorInHTTPResponse:
197     case ErrorCode::kDownloadOperationHashMissingError:
198     case ErrorCode::kDownloadMetadataSignatureMissingError:
199     case ErrorCode::kOmahaUpdateDeferredForBackoff:
200     case ErrorCode::kPostinstallPowerwashError:
201     case ErrorCode::kUpdateCanceledByChannelChange:
202     case ErrorCode::kPostinstallFirmwareRONotUpdatable:
203     case ErrorCode::kUnsupportedMajorPayloadVersion:
204     case ErrorCode::kUnsupportedMinorPayloadVersion:
205     case ErrorCode::kOmahaRequestXMLHasEntityDecl:
206     case ErrorCode::kFilesystemVerifierError:
207     case ErrorCode::kUserCanceled:
208       break;
209 
210     // Special flags. These can't happen (we mask them out above) but
211     // the compiler doesn't know that. Just break out so we can warn and
212     // return |kInputMalformed|.
213     case ErrorCode::kUmaReportedMax:
214     case ErrorCode::kOmahaRequestHTTPResponseBase:
215     case ErrorCode::kDevModeFlag:
216     case ErrorCode::kResumedFlag:
217     case ErrorCode::kTestImageFlag:
218     case ErrorCode::kTestOmahaUrlFlag:
219     case ErrorCode::kSpecialFlags:
220       LOG(ERROR) << "Unexpected error code " << base_code;
221       break;
222   }
223 
224   return metrics::DownloadErrorCode::kInputMalformed;
225 }
226 
GetConnectionType(NetworkConnectionType type,NetworkTethering tethering)227 metrics::ConnectionType GetConnectionType(NetworkConnectionType type,
228                                           NetworkTethering tethering) {
229   switch (type) {
230     case NetworkConnectionType::kUnknown:
231       return metrics::ConnectionType::kUnknown;
232 
233     case NetworkConnectionType::kEthernet:
234       if (tethering == NetworkTethering::kConfirmed)
235         return metrics::ConnectionType::kTetheredEthernet;
236       else
237         return metrics::ConnectionType::kEthernet;
238 
239     case NetworkConnectionType::kWifi:
240       if (tethering == NetworkTethering::kConfirmed)
241         return metrics::ConnectionType::kTetheredWifi;
242       else
243         return metrics::ConnectionType::kWifi;
244 
245     case NetworkConnectionType::kWimax:
246       return metrics::ConnectionType::kWimax;
247 
248     case NetworkConnectionType::kBluetooth:
249       return metrics::ConnectionType::kBluetooth;
250 
251     case NetworkConnectionType::kCellular:
252       return metrics::ConnectionType::kCellular;
253   }
254 
255   LOG(ERROR) << "Unexpected network connection type: type="
256              << static_cast<int>(type)
257              << ", tethering=" << static_cast<int>(tethering);
258 
259   return metrics::ConnectionType::kUnknown;
260 }
261 
WallclockDurationHelper(SystemState * system_state,const std::string & state_variable_key,TimeDelta * out_duration)262 bool WallclockDurationHelper(SystemState* system_state,
263                              const std::string& state_variable_key,
264                              TimeDelta* out_duration) {
265   bool ret = false;
266 
267   Time now = system_state->clock()->GetWallclockTime();
268   int64_t stored_value;
269   if (system_state->prefs()->GetInt64(state_variable_key, &stored_value)) {
270     Time stored_time = Time::FromInternalValue(stored_value);
271     if (stored_time > now) {
272       LOG(ERROR) << "Stored time-stamp used for " << state_variable_key
273                  << " is in the future.";
274     } else {
275       *out_duration = now - stored_time;
276       ret = true;
277     }
278   }
279 
280   if (!system_state->prefs()->SetInt64(state_variable_key,
281                                        now.ToInternalValue())) {
282     LOG(ERROR) << "Error storing time-stamp in " << state_variable_key;
283   }
284 
285   return ret;
286 }
287 
MonotonicDurationHelper(SystemState * system_state,int64_t * storage,TimeDelta * out_duration)288 bool MonotonicDurationHelper(SystemState* system_state,
289                              int64_t* storage,
290                              TimeDelta* out_duration) {
291   bool ret = false;
292 
293   Time now = system_state->clock()->GetMonotonicTime();
294   if (*storage != 0) {
295     Time stored_time = Time::FromInternalValue(*storage);
296     *out_duration = now - stored_time;
297     ret = true;
298   }
299   *storage = now.ToInternalValue();
300 
301   return ret;
302 }
303 
304 }  // namespace metrics_utils
305 }  // namespace chromeos_update_engine
306