You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
311 lines
12 KiB
311 lines
12 KiB
// |
|
// 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: |
|
case ErrorCode::kPayloadTimestampError: |
|
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: |
|
// TODO(deymo): The next two items belong in their own category; they |
|
// should not be counted as internal errors. b/27112092 |
|
case ErrorCode::kOmahaUpdateDeferredPerPolicy: |
|
case ErrorCode::kNonCriticalUpdateInOOBE: |
|
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::kNonCriticalUpdateInOOBE: |
|
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: |
|
case ErrorCode::kPayloadTimestampError: |
|
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(ConnectionType type, |
|
ConnectionTethering tethering) { |
|
switch (type) { |
|
case ConnectionType::kUnknown: |
|
return metrics::ConnectionType::kUnknown; |
|
|
|
case ConnectionType::kEthernet: |
|
if (tethering == ConnectionTethering::kConfirmed) |
|
return metrics::ConnectionType::kTetheredEthernet; |
|
else |
|
return metrics::ConnectionType::kEthernet; |
|
|
|
case ConnectionType::kWifi: |
|
if (tethering == ConnectionTethering::kConfirmed) |
|
return metrics::ConnectionType::kTetheredWifi; |
|
else |
|
return metrics::ConnectionType::kWifi; |
|
|
|
case ConnectionType::kWimax: |
|
return metrics::ConnectionType::kWimax; |
|
|
|
case ConnectionType::kBluetooth: |
|
return metrics::ConnectionType::kBluetooth; |
|
|
|
case ConnectionType::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
|
|
|