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 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 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 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 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 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 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 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 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 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 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 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