|  | // | 
|  | // Copyright (C) 2015 The Android Open Source Project | 
|  | // | 
|  | // Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | // you may not use this file except in compliance with the License. | 
|  | // You may obtain a copy of the License at | 
|  | // | 
|  | //      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | // | 
|  | // Unless required by applicable law or agreed to in writing, software | 
|  | // distributed under the License is distributed on an "AS IS" BASIS, | 
|  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | // See the License for the specific language governing permissions and | 
|  | // limitations under the License. | 
|  | // | 
|  |  | 
|  | #include "update_engine/metrics_utils.h" | 
|  |  | 
|  | #include <string> | 
|  |  | 
|  | #include <base/time/time.h> | 
|  |  | 
|  | #include "update_engine/common/clock_interface.h" | 
|  | #include "update_engine/common/prefs_interface.h" | 
|  | #include "update_engine/system_state.h" | 
|  |  | 
|  | using base::Time; | 
|  | using base::TimeDelta; | 
|  |  | 
|  | namespace chromeos_update_engine { | 
|  | namespace metrics_utils { | 
|  |  | 
|  | metrics::AttemptResult GetAttemptResult(ErrorCode code) { | 
|  | ErrorCode base_code = static_cast<ErrorCode>( | 
|  | static_cast<int>(code) & ~static_cast<int>(ErrorCode::kSpecialFlags)); | 
|  |  | 
|  | switch (base_code) { | 
|  | case ErrorCode::kSuccess: | 
|  | return metrics::AttemptResult::kUpdateSucceeded; | 
|  |  | 
|  | case ErrorCode::kDownloadTransferError: | 
|  | return metrics::AttemptResult::kPayloadDownloadError; | 
|  |  | 
|  | case ErrorCode::kDownloadInvalidMetadataSize: | 
|  | case ErrorCode::kDownloadInvalidMetadataMagicString: | 
|  | case ErrorCode::kDownloadMetadataSignatureError: | 
|  | case ErrorCode::kDownloadMetadataSignatureVerificationError: | 
|  | case ErrorCode::kPayloadMismatchedType: | 
|  | case ErrorCode::kUnsupportedMajorPayloadVersion: | 
|  | case ErrorCode::kUnsupportedMinorPayloadVersion: | 
|  | case ErrorCode::kDownloadNewPartitionInfoError: | 
|  | case ErrorCode::kDownloadSignatureMissingInManifest: | 
|  | case ErrorCode::kDownloadManifestParseError: | 
|  | case ErrorCode::kDownloadOperationHashMissingError: | 
|  | return metrics::AttemptResult::kMetadataMalformed; | 
|  |  | 
|  | case ErrorCode::kDownloadOperationHashMismatch: | 
|  | case ErrorCode::kDownloadOperationHashVerificationError: | 
|  | return metrics::AttemptResult::kOperationMalformed; | 
|  |  | 
|  | case ErrorCode::kDownloadOperationExecutionError: | 
|  | case ErrorCode::kInstallDeviceOpenError: | 
|  | case ErrorCode::kKernelDeviceOpenError: | 
|  | case ErrorCode::kDownloadWriteError: | 
|  | case ErrorCode::kFilesystemCopierError: | 
|  | case ErrorCode::kFilesystemVerifierError: | 
|  | return metrics::AttemptResult::kOperationExecutionError; | 
|  |  | 
|  | case ErrorCode::kDownloadMetadataSignatureMismatch: | 
|  | return metrics::AttemptResult::kMetadataVerificationFailed; | 
|  |  | 
|  | case ErrorCode::kPayloadSizeMismatchError: | 
|  | case ErrorCode::kPayloadHashMismatchError: | 
|  | case ErrorCode::kDownloadPayloadVerificationError: | 
|  | case ErrorCode::kSignedDeltaPayloadExpectedError: | 
|  | case ErrorCode::kDownloadPayloadPubKeyVerificationError: | 
|  | return metrics::AttemptResult::kPayloadVerificationFailed; | 
|  |  | 
|  | case ErrorCode::kNewRootfsVerificationError: | 
|  | case ErrorCode::kNewKernelVerificationError: | 
|  | return metrics::AttemptResult::kVerificationFailed; | 
|  |  | 
|  | case ErrorCode::kPostinstallRunnerError: | 
|  | case ErrorCode::kPostinstallBootedFromFirmwareB: | 
|  | case ErrorCode::kPostinstallFirmwareRONotUpdatable: | 
|  | return metrics::AttemptResult::kPostInstallFailed; | 
|  |  | 
|  | case ErrorCode::kUserCanceled: | 
|  | return metrics::AttemptResult::kUpdateCanceled; | 
|  |  | 
|  | // We should never get these errors in the update-attempt stage so | 
|  | // return internal error if this happens. | 
|  | case ErrorCode::kError: | 
|  | case ErrorCode::kOmahaRequestXMLParseError: | 
|  | case ErrorCode::kOmahaRequestError: | 
|  | case ErrorCode::kOmahaResponseHandlerError: | 
|  | case ErrorCode::kDownloadStateInitializationError: | 
|  | case ErrorCode::kOmahaRequestEmptyResponseError: | 
|  | case ErrorCode::kDownloadInvalidMetadataSignature: | 
|  | case ErrorCode::kOmahaResponseInvalid: | 
|  | case ErrorCode::kOmahaUpdateIgnoredPerPolicy: | 
|  | case ErrorCode::kOmahaUpdateDeferredPerPolicy: | 
|  | case ErrorCode::kOmahaErrorInHTTPResponse: | 
|  | case ErrorCode::kDownloadMetadataSignatureMissingError: | 
|  | case ErrorCode::kOmahaUpdateDeferredForBackoff: | 
|  | case ErrorCode::kPostinstallPowerwashError: | 
|  | case ErrorCode::kUpdateCanceledByChannelChange: | 
|  | case ErrorCode::kOmahaRequestXMLHasEntityDecl: | 
|  | return metrics::AttemptResult::kInternalError; | 
|  |  | 
|  | // Special flags. These can't happen (we mask them out above) but | 
|  | // the compiler doesn't know that. Just break out so we can warn and | 
|  | // return |kInternalError|. | 
|  | case ErrorCode::kUmaReportedMax: | 
|  | case ErrorCode::kOmahaRequestHTTPResponseBase: | 
|  | case ErrorCode::kDevModeFlag: | 
|  | case ErrorCode::kResumedFlag: | 
|  | case ErrorCode::kTestImageFlag: | 
|  | case ErrorCode::kTestOmahaUrlFlag: | 
|  | case ErrorCode::kSpecialFlags: | 
|  | break; | 
|  | } | 
|  |  | 
|  | LOG(ERROR) << "Unexpected error code " << base_code; | 
|  | return metrics::AttemptResult::kInternalError; | 
|  | } | 
|  |  | 
|  | metrics::DownloadErrorCode GetDownloadErrorCode(ErrorCode code) { | 
|  | ErrorCode base_code = static_cast<ErrorCode>( | 
|  | static_cast<int>(code) & ~static_cast<int>(ErrorCode::kSpecialFlags)); | 
|  |  | 
|  | if (base_code >= ErrorCode::kOmahaRequestHTTPResponseBase) { | 
|  | int http_status = | 
|  | static_cast<int>(base_code) - | 
|  | static_cast<int>(ErrorCode::kOmahaRequestHTTPResponseBase); | 
|  | if (http_status >= 200 && http_status <= 599) { | 
|  | return static_cast<metrics::DownloadErrorCode>( | 
|  | static_cast<int>(metrics::DownloadErrorCode::kHttpStatus200) + | 
|  | http_status - 200); | 
|  | } else if (http_status == 0) { | 
|  | // The code is using HTTP Status 0 for "Unable to get http | 
|  | // response code." | 
|  | return metrics::DownloadErrorCode::kDownloadError; | 
|  | } | 
|  | LOG(WARNING) << "Unexpected HTTP status code " << http_status; | 
|  | return metrics::DownloadErrorCode::kHttpStatusOther; | 
|  | } | 
|  |  | 
|  | switch (base_code) { | 
|  | // Unfortunately, ErrorCode::kDownloadTransferError is returned for a wide | 
|  | // variety of errors (proxy errors, host not reachable, timeouts etc.). | 
|  | // | 
|  | // For now just map that to kDownloading. See http://crbug.com/355745 | 
|  | // for how we plan to add more detail in the future. | 
|  | case ErrorCode::kDownloadTransferError: | 
|  | return metrics::DownloadErrorCode::kDownloadError; | 
|  |  | 
|  | // All of these error codes are not related to downloading so break | 
|  | // out so we can warn and return InputMalformed. | 
|  | case ErrorCode::kSuccess: | 
|  | case ErrorCode::kError: | 
|  | case ErrorCode::kOmahaRequestError: | 
|  | case ErrorCode::kOmahaResponseHandlerError: | 
|  | case ErrorCode::kFilesystemCopierError: | 
|  | case ErrorCode::kPostinstallRunnerError: | 
|  | case ErrorCode::kPayloadMismatchedType: | 
|  | case ErrorCode::kInstallDeviceOpenError: | 
|  | case ErrorCode::kKernelDeviceOpenError: | 
|  | case ErrorCode::kPayloadHashMismatchError: | 
|  | case ErrorCode::kPayloadSizeMismatchError: | 
|  | case ErrorCode::kDownloadPayloadVerificationError: | 
|  | case ErrorCode::kDownloadNewPartitionInfoError: | 
|  | case ErrorCode::kDownloadWriteError: | 
|  | case ErrorCode::kNewRootfsVerificationError: | 
|  | case ErrorCode::kNewKernelVerificationError: | 
|  | case ErrorCode::kSignedDeltaPayloadExpectedError: | 
|  | case ErrorCode::kDownloadPayloadPubKeyVerificationError: | 
|  | case ErrorCode::kPostinstallBootedFromFirmwareB: | 
|  | case ErrorCode::kDownloadStateInitializationError: | 
|  | case ErrorCode::kDownloadInvalidMetadataMagicString: | 
|  | case ErrorCode::kDownloadSignatureMissingInManifest: | 
|  | case ErrorCode::kDownloadManifestParseError: | 
|  | case ErrorCode::kDownloadMetadataSignatureError: | 
|  | case ErrorCode::kDownloadMetadataSignatureVerificationError: | 
|  | case ErrorCode::kDownloadMetadataSignatureMismatch: | 
|  | case ErrorCode::kDownloadOperationHashVerificationError: | 
|  | case ErrorCode::kDownloadOperationExecutionError: | 
|  | case ErrorCode::kDownloadOperationHashMismatch: | 
|  | case ErrorCode::kOmahaRequestEmptyResponseError: | 
|  | case ErrorCode::kOmahaRequestXMLParseError: | 
|  | case ErrorCode::kDownloadInvalidMetadataSize: | 
|  | case ErrorCode::kDownloadInvalidMetadataSignature: | 
|  | case ErrorCode::kOmahaResponseInvalid: | 
|  | case ErrorCode::kOmahaUpdateIgnoredPerPolicy: | 
|  | case ErrorCode::kOmahaUpdateDeferredPerPolicy: | 
|  | case ErrorCode::kOmahaErrorInHTTPResponse: | 
|  | case ErrorCode::kDownloadOperationHashMissingError: | 
|  | case ErrorCode::kDownloadMetadataSignatureMissingError: | 
|  | case ErrorCode::kOmahaUpdateDeferredForBackoff: | 
|  | case ErrorCode::kPostinstallPowerwashError: | 
|  | case ErrorCode::kUpdateCanceledByChannelChange: | 
|  | case ErrorCode::kPostinstallFirmwareRONotUpdatable: | 
|  | case ErrorCode::kUnsupportedMajorPayloadVersion: | 
|  | case ErrorCode::kUnsupportedMinorPayloadVersion: | 
|  | case ErrorCode::kOmahaRequestXMLHasEntityDecl: | 
|  | case ErrorCode::kFilesystemVerifierError: | 
|  | case ErrorCode::kUserCanceled: | 
|  | break; | 
|  |  | 
|  | // Special flags. These can't happen (we mask them out above) but | 
|  | // the compiler doesn't know that. Just break out so we can warn and | 
|  | // return |kInputMalformed|. | 
|  | case ErrorCode::kUmaReportedMax: | 
|  | case ErrorCode::kOmahaRequestHTTPResponseBase: | 
|  | case ErrorCode::kDevModeFlag: | 
|  | case ErrorCode::kResumedFlag: | 
|  | case ErrorCode::kTestImageFlag: | 
|  | case ErrorCode::kTestOmahaUrlFlag: | 
|  | case ErrorCode::kSpecialFlags: | 
|  | LOG(ERROR) << "Unexpected error code " << base_code; | 
|  | break; | 
|  | } | 
|  |  | 
|  | return metrics::DownloadErrorCode::kInputMalformed; | 
|  | } | 
|  |  | 
|  | metrics::ConnectionType GetConnectionType(NetworkConnectionType type, | 
|  | NetworkTethering tethering) { | 
|  | switch (type) { | 
|  | case NetworkConnectionType::kUnknown: | 
|  | return metrics::ConnectionType::kUnknown; | 
|  |  | 
|  | case NetworkConnectionType::kEthernet: | 
|  | if (tethering == NetworkTethering::kConfirmed) | 
|  | return metrics::ConnectionType::kTetheredEthernet; | 
|  | else | 
|  | return metrics::ConnectionType::kEthernet; | 
|  |  | 
|  | case NetworkConnectionType::kWifi: | 
|  | if (tethering == NetworkTethering::kConfirmed) | 
|  | return metrics::ConnectionType::kTetheredWifi; | 
|  | else | 
|  | return metrics::ConnectionType::kWifi; | 
|  |  | 
|  | case NetworkConnectionType::kWimax: | 
|  | return metrics::ConnectionType::kWimax; | 
|  |  | 
|  | case NetworkConnectionType::kBluetooth: | 
|  | return metrics::ConnectionType::kBluetooth; | 
|  |  | 
|  | case NetworkConnectionType::kCellular: | 
|  | return metrics::ConnectionType::kCellular; | 
|  | } | 
|  |  | 
|  | LOG(ERROR) << "Unexpected network connection type: type=" | 
|  | << static_cast<int>(type) | 
|  | << ", tethering=" << static_cast<int>(tethering); | 
|  |  | 
|  | return metrics::ConnectionType::kUnknown; | 
|  | } | 
|  |  | 
|  | bool WallclockDurationHelper(SystemState* system_state, | 
|  | const std::string& state_variable_key, | 
|  | TimeDelta* out_duration) { | 
|  | bool ret = false; | 
|  |  | 
|  | Time now = system_state->clock()->GetWallclockTime(); | 
|  | int64_t stored_value; | 
|  | if (system_state->prefs()->GetInt64(state_variable_key, &stored_value)) { | 
|  | Time stored_time = Time::FromInternalValue(stored_value); | 
|  | if (stored_time > now) { | 
|  | LOG(ERROR) << "Stored time-stamp used for " << state_variable_key | 
|  | << " is in the future."; | 
|  | } else { | 
|  | *out_duration = now - stored_time; | 
|  | ret = true; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!system_state->prefs()->SetInt64(state_variable_key, | 
|  | now.ToInternalValue())) { | 
|  | LOG(ERROR) << "Error storing time-stamp in " << state_variable_key; | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | bool MonotonicDurationHelper(SystemState* system_state, | 
|  | int64_t* storage, | 
|  | TimeDelta* out_duration) { | 
|  | bool ret = false; | 
|  |  | 
|  | Time now = system_state->clock()->GetMonotonicTime(); | 
|  | if (*storage != 0) { | 
|  | Time stored_time = Time::FromInternalValue(*storage); | 
|  | *out_duration = now - stored_time; | 
|  | ret = true; | 
|  | } | 
|  | *storage = now.ToInternalValue(); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | }  // namespace metrics_utils | 
|  | }  // namespace chromeos_update_engine |