diff --git a/libwvdrmengine/include/wv_metrics.h b/libwvdrmengine/include/wv_metrics.h index 6dfe7297..e2e8c9a3 100644 --- a/libwvdrmengine/include/wv_metrics.h +++ b/libwvdrmengine/include/wv_metrics.h @@ -7,13 +7,14 @@ #ifndef WV_METRICS_H_ #define WV_METRICS_H_ -#include +#include #include "wv_metrics.pb.h" namespace wv_metrics { void FormatWvCdmMetrics(const drm_metrics::WvCdmMetrics& metrics, - std::string& result); + size_t parent_indent, + std::ostream& os); } #endif // WV_METRICS_H_ diff --git a/libwvdrmengine/src/WVDrmFactory.cpp b/libwvdrmengine/src/WVDrmFactory.cpp index f33be24a..412e8bc2 100644 --- a/libwvdrmengine/src/WVDrmFactory.cpp +++ b/libwvdrmengine/src/WVDrmFactory.cpp @@ -43,6 +43,41 @@ using ::aidl::android::hardware::drm::Uuid; WVGenericCryptoInterface WVDrmFactory::sOemCryptoInterface; +namespace { +void PrintStream(int fd, std::stringstream& ss) { + const std::string content = ss.str(); + dprintf(fd, "%s", content.c_str()); +} + +void FormatIndent(int fd, size_t indent) { + const size_t kMaxIndent = 60; + const size_t real_indent = std::min(indent, kMaxIndent); + char buffer[kMaxIndent + 1]; + memset(buffer, ' ', real_indent); + buffer[real_indent] = 0; + dprintf(fd, "%s", buffer); + if (real_indent < indent) FormatIndent(fd, indent - real_indent); +} + +void FormatWvMetricsSnapshotItem(int fd, size_t parent_indent, + drm_metrics::WvCdmMetrics& metrics, + size_t item_index) { + const size_t indent = parent_indent + 2; + // TODO(b/239462891): Provide identifier and timestamp for metrics. + + // Serialized proto size. + FormatIndent(fd, parent_indent); // First parameter uses indent + list tick. + dprintf(fd, "- serialized_proto_bytes: %zu # [%zu]\n", + metrics.ByteSizeLong(), item_index); + + FormatIndent(fd, indent); + dprintf(fd, "cdm_metrics:\n"); + std::stringstream ss; + wv_metrics::FormatWvCdmMetrics(metrics, indent, ss); + PrintStream(fd, ss); +} +} // namespace + bool WVDrmFactory::isCryptoSchemeSupported(const Uuid& in_uuid) { return isWidevineUUID(in_uuid.uuid.data()); } @@ -182,25 +217,30 @@ void WVDrmFactory::printCdmMetrics(int fd) { bool full_list_returned = true; wvcdm::CdmResponseType result = cdm->GetMetrics(&metrics, &full_list_returned); - if (metrics.empty()) { - dprintf(fd, - " error_message: Metrics not available, please retry while streaming a video.\n"); - } else if (!full_list_returned) { - dprintf(fd, - " error_message: Not all metrics are returned due to some GetMetric error, " - "please check logcat for possible GetMetric errors.\n"); - } - if (result == wvcdm::NO_ERROR) { - for (auto& metric : metrics) { - dprintf(fd, " - serialized_proto_bytes: %zu\n", metric.ByteSizeLong()); - string formatted; - wv_metrics::FormatWvCdmMetrics(metric, formatted); - dprintf(fd, "%s\n", formatted.c_str()); - } + if (result != wvcdm::NO_ERROR) { + dprintf(fd, " live_metrics:\n"); + dprintf(fd, " error_message: \"%s\"\n", result.ToString().c_str()); + dprintf(fd, " error_code: %d\n", static_cast(result.code())); + } else if (metrics.empty()) { + // YAML does not support empty property values. + const char kNoMetricsMessage[] = + "Metrics not available, please retry while streaming a video."; + dprintf(fd, " live_metrics: [] # %s\n", kNoMetricsMessage); } else { - dprintf(fd, " error_message: %s\n", result.GetCodeString()); - dprintf(fd, " error_code: %d\n", result.ToInt()); + dprintf(fd, " live_metrics: "); + if (full_list_returned) { + dprintf(fd, "# count = %zu\n", metrics.size()); + } else { + const char kPartialListMessage[] = + "Some metrics are missing due to an internal error, " + "check logs for details."; + dprintf(fd, "# count = %zu, %s\n", metrics.size(), kPartialListMessage); + } + for (size_t i = 0; i < metrics.size(); i++) { + FormatWvMetricsSnapshotItem(fd, 2, metrics[i], i); + } } + // TODO(b/270166158): Print metrics history. } void WVDrmFactory::printCdmProperties(int fd) { diff --git a/libwvdrmengine/src/wv_metrics.cpp b/libwvdrmengine/src/wv_metrics.cpp index 8f0aca10..73f7f0bd 100644 --- a/libwvdrmengine/src/wv_metrics.cpp +++ b/libwvdrmengine/src/wv_metrics.cpp @@ -6,364 +6,460 @@ #include "wv_metrics.h" -#include -#include -#include -#include -#include +#include #include #include "OEMCryptoCENC.h" +#include "metrics_collections.h" +#include "string_conversions.h" #include "wv_cdm_types.h" using namespace drm_metrics; using ::aidl::android::hardware::drm::Status; -using std::string; -using std::to_string; +using ::wvcdm::metrics::OEMCryptoInitializationMode; namespace { -const string kIndentSpaces = " "; +constexpr size_t kIndentIncrement = 2; -const string DrmHalStatusToString(Status hal_status) { - switch (hal_status) { - case Status::OK: - return "OK"; - case Status::ERROR_DRM_NO_LICENSE: - return "ERROR_DRM_NO_LICENSE"; - case Status::ERROR_DRM_LICENSE_EXPIRED: - return "ERROR_DRM_LICENSE_EXPIRED"; - case Status::ERROR_DRM_SESSION_NOT_OPENED: - return "ERROR_DRM_SESSION_NOT_OPENED"; - case Status::ERROR_DRM_CANNOT_HANDLE: - return "ERROR_DRM_CANNOT_HANDLE"; - case Status::ERROR_DRM_INVALID_STATE: - return "ERROR_DRM_INVALID_STATE,"; - case Status::BAD_VALUE: - return "BAD_VALUE"; - case Status::ERROR_DRM_NOT_PROVISIONED: - return "ERROR_DRM_NOT_PROVISIONED"; - case Status::ERROR_DRM_RESOURCE_BUSY: - return "ERROR_DRM_RESOURCE_BUSY"; - case Status::ERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION: - return "ERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION"; - case Status::ERROR_DRM_DEVICE_REVOKED: - return "ERROR_DRM_DEVICE_REVOKED"; - case Status::ERROR_DRM_DECRYPT: - return "ERROR_DRM_DECRYPT"; - case Status::ERROR_DRM_UNKNOWN: - return "ERROR_DRM_UNKNOWN"; - case Status::ERROR_DRM_INSUFFICIENT_SECURITY: - return "ERROR_DRM_INSUFFICIENT_SECURITY"; - case Status::ERROR_DRM_FRAME_TOO_LARGE: - return "ERROR_DRM_FRAME_TOO_LARGE"; - case Status::ERROR_DRM_SESSION_LOST_STATE: - return "ERROR_DRM_SESSION_LOST_STATE"; - case Status::ERROR_DRM_RESOURCE_CONTENTION: - return "ERROR_DRM_RESOURCE_CONTENTION"; - case Status::CANNOT_DECRYPT_ZERO_SUBSAMPLES: - return "CANNOT_DECRYPT_ZERO_SUBSAMPLES"; - case Status::CRYPTO_LIBRARY_ERROR: - return "CRYPTO_LIBRARY_ERROR"; - case Status::GENERAL_OEM_ERROR: - return "GENERAL_OEM_ERROR"; - case Status::GENERAL_PLUGIN_ERROR: - return "GENERAL_PLUGIN_ERROR"; - case Status::INIT_DATA_INVALID: - return "INIT_DATA_INVALID"; - case Status::KEY_NOT_LOADED: - return "KEY_NOT_LOADED"; - case Status::LICENSE_PARSE_ERROR: - return "LICENSE_PARSE_ERROR"; - case Status::LICENSE_POLICY_ERROR: - return "LICENSE_POLICY_ERROR"; - case Status::LICENSE_RELEASE_ERROR: - return "LICENSE_RELEASE_ERROR"; - case Status::LICENSE_REQUEST_REJECTED: - return "LICENSE_REQUEST_REJECTED"; - case Status::LICENSE_RESTORE_ERROR: - return "LICENSE_RESTORE_ERROR"; - case Status::LICENSE_STATE_ERROR: - return "LICENSE_STATE_ERROR"; - case Status::MALFORMED_CERTIFICATE: - return "MALFORMED_CERTIFICATE"; - case Status::MEDIA_FRAMEWORK_ERROR: - return "MEDIA_FRAMEWORK_ERROR"; - case Status::MISSING_CERTIFICATE: - return "MISSING_CERTIFICATE"; - case Status::PROVISIONING_CERTIFICATE_ERROR: - return "PROVISIONING_CERTIFICATE_ERROR"; - case Status::PROVISIONING_CONFIGURATION_ERROR: - return "PROVISIONING_CONFIGURATION_ERROR"; - case Status::PROVISIONING_PARSE_ERROR: - return "PROVISIONING_PARSE_ERROR"; - case Status::PROVISIONING_REQUEST_REJECTED: - return "PROVISIONING_REQUEST_REJECTED"; - case Status::RETRYABLE_PROVISIONING_ERROR: - return "RETRYABLE_PROVISIONING_ERROR"; - case Status::SECURE_STOP_RELEASE_ERROR: - return "SECURE_STOP_RELEASE_ERROR"; - case Status::STORAGE_READ_FAILURE: - return "STORAGE_READ_FAILURE"; - case Status::STORAGE_WRITE_FAILURE: - return "STORAGE_WRITE_FAILURE"; - default: - return "Invalid Drm HAL status."; +void FormatIndent(size_t indent, std::ostream& os) { + for (size_t i = 0; i < indent; i++) os.put(' '); +} + +void FormatListIndent(size_t parent_indent, std::ostream& os) { + FormatIndent(parent_indent, os); + os.put('-'); + FormatIndent(kIndentIncrement - 1, os); +} + +void FormatString(const std::string& s, std::ostream& os) { + os << '"'; + if (std::any_of(s.begin(), s.end(), [](char c) { return c == '"'; })) { + // Escape any double quotes. + for (char c : s) { + if (c == '"') { + os << "\\\""; + } else { + os << c; + } + } + } else { + os << s; + } + os << '"'; +} + +void FormatString(const char* s, std::ostream& os) { + if (s == nullptr) { + os << "null"; + } else { + FormatString(std::string(s), os); } } -string FormatOEMCryptoInitializeMode(const ValueMetric& vm) { - std::map translations = { - {0, "USING_IN_APP"}, - {1, "FORCING_L3"}, - {2, "USING_L3_NO_L1_LIBRARY_PATH"}, - {3, "USING_L3_L1_OPEN_FAILED"}, - {4, "USING_L3_L1_LOAD_FAILED"}, - {5, "USING_L3_COULD_NOT_INITIALIZE_L1"}, - {6, "USING_L3_WRONG_L1_VERSION"}, - {7, "USING_L1_WITH_KEYBOX"}, - {8, "USING_L1_WITH_CERTIFICATE"}, - {9, "USING_L1_CERTIFICATE_MIX"}, - {10, "USING_L3_BAD_KEYBOX"}, - {11, "USING_L3_COULD_NOT_OPEN_FACTORY_KEYBOX"}, - {12, "USING_L3_COULD_NOT_INSTALL_KEYBOX"}, - {13, "USING_L1_INSTALLED_KEYBOX"}, - {14, "USING_L3_INVALID_L1"}, - {15, "USING_L1_WITH_PROVISIONING_3_0"}, - {16, "L3_INITIALIZATION_GENERAL_FAILED"}, - {17, "L3_INITIALIZATION_RNG_FAILED"}, - {18, "L3_INITIALIZATION_SAVE_DEVICE_KEYS_FAILED"}, - {19, "L3_INITIALIZATION_READ_DEVICE_KEYS_FAILED"}, - {20, "L3_INITIALIZATION_VERIFY_DEVIE_KEYS_FAILED"}}; - return translations[vm.int_value()]; +const char* OEMCryptoInitializeModeToString(OEMCryptoInitializationMode mode) { + using namespace wvcdm::metrics; + switch (mode) { + case OEMCrypto_INITIALIZED_USING_IN_APP: + return "USING_IN_APP"; + case OEMCrypto_INITIALIZED_FORCING_L3: + return "FORCING_L3"; + case OEMCrypto_INITIALIZED_USING_L3_NO_L1_LIBRARY_PATH: + return "USING_L3_NO_L1_LIBRARY_PATH"; + case OEMCrypto_INITIALIZED_USING_L3_L1_OPEN_FAILED: + return "USING_L3_L1_OPEN_FAILED"; + case OEMCrypto_INITIALIZED_USING_L3_L1_LOAD_FAILED: + return "USING_L3_L1_LOAD_FAILED"; + case OEMCrypto_INITIALIZED_USING_L3_COULD_NOT_INITIALIZE_L1: + return "USING_L3_COULD_NOT_INITIALIZE_L1"; + case OEMCrypto_INITIALIZED_USING_L3_WRONG_L1_VERSION: + return "USING_L3_WRONG_L1_VERSION"; + case OEMCrypto_INITIALIZED_USING_L1_WITH_KEYBOX: + return "USING_L1_WITH_KEYBOX"; + case OEMCrypto_INITIALIZED_USING_L1_WITH_CERTIFICATE: + return "USING_L1_WITH_CERTIFICATE"; + case OEMCrypto_INITIALIZED_USING_L1_CERTIFICATE_MIX: + return "USING_L1_CERTIFICATE_MIX"; + case OEMCrypto_INITIALIZED_USING_L3_BAD_KEYBOX: + return "USING_L3_BAD_KEYBOX"; + case OEMCrypto_INITIALIZED_USING_L3_COULD_NOT_OPEN_FACTORY_KEYBOX: + return "USING_L3_COULD_NOT_OPEN_FACTORY_KEYBOX"; + case OEMCrypto_INITIALIZED_USING_L3_COULD_NOT_INSTALL_KEYBOX: + return "USING_L3_COULD_NOT_INSTALL_KEYBOX"; + case OEMCrypto_INITIALIZED_USING_L1_INSTALLED_KEYBOX: + return "USING_L1_INSTALLED_KEYBOX"; + case OEMCrypto_INITIALIZED_USING_L3_INVALID_L1: + return "USING_L3_INVALID_L1"; + case OEMCrypto_INITIALIZED_USING_L1_WITH_PROVISIONING_3_0: + return "USING_L1_WITH_PROVISIONING_3_0"; + case OEMCrypto_INITIALIZED_L3_INITIALIZATION_FAILED: + return "L3_INITIALIZATION_FAILED"; + case OEMCrypto_INITIALIZED_L3_RNG_FAILED: + return "L3_RNG_FAILED"; + case OEMCrypto_INITIALIZED_L3_SAVE_DEVICE_KEYS_FAILED: + return "L3_SAVE_DEVICE_KEYS_FAILED"; + case OEMCrypto_INITIALIZED_L3_READ_DEVICE_KEYS_FAILED: + return "L3_READ_DEVICE_KEYS_FAILED"; + case OEMCrypto_INITIALIZED_L3_VERIFY_DEVICE_KEYS_FAILED: + return "L3_VERIFY_DEVICE_KEYS_FAILED"; + case OEMCrypto_INITIALIZED_USING_L1_WITH_PROVISIONING_4_0: + return "USING_L1_WITH_PROVISIONING_4_0"; + } + return wvcdm::UnknownEnumValueToString(static_cast(mode)); } -string FormatOEMCryptoHdcpCapability(const ValueMetric& vm) { - std::map translations = { - {0, "HDCP_NONE"}, {1, "HDCP_V1"}, {2, "HDCP_V2"}, - {3, "HDCP_V2_1"}, {4, "HDCP_V2_2"}, {5, "HDCP_V2_3"}, - {0xff, "NO_DIGITAL_OUTPUT"}}; - return translations[vm.int_value()]; +const char* OEMCryptoHdcpCapabilityToString( + OEMCrypto_HDCP_Capability hdcp_level) { + switch (hdcp_level) { + case HDCP_NONE: + return "HDCP_NONE"; + case HDCP_V1: + return "HDCP_V1"; + case HDCP_V2: + return "HDCP_V2"; + case HDCP_V2_1: + return "HDCP_V2_1"; + case HDCP_V2_2: + return "HDCP_V2_2"; + case HDCP_V2_3: + return "HDCP_V2_3"; + case HDCP_V1_0: + return "HDCP_V1_0"; + case HDCP_V1_1: + return "HDCP_V1_1"; + case HDCP_V1_2: + return "HDCP_V1_2"; + case HDCP_V1_3: + return "HDCP_V1_3"; + case HDCP_V1_4: + return "HDCP_V1_4"; + case HDCP_NO_DIGITAL_OUTPUT: + return "HDCP_NO_DIGITAL_OUTPUT"; + } + return wvcdm::UnknownEnumValueToString(static_cast(hdcp_level)); } -string FormatOEMCryptoProvisioningMethod(const ValueMetric& vm) { - std::map translations = {{0, "PROVISIONING_ERROR"}, - {1, "DRM_CERTIFICATE"}, - {2, "KEYBOX"}, - {3, "OEM_CERTIFICATE"}}; - return translations[vm.int_value()]; +const char* OEMCryptoProvisioningMethodToString( + OEMCrypto_ProvisioningMethod method) { + switch (method) { + case OEMCrypto_ProvisioningError: + return "PROVISIONING_ERROR"; + case OEMCrypto_DrmCertificate: + return "DRM_CERTIFICATE"; + case OEMCrypto_Keybox: + return "KEYBOX"; + case OEMCrypto_OEMCertificate: + return "OEM_CERTIFICATE"; + case OEMCrypto_BootCertificateChain: + return "BOOT_CERTIFICATE_CHAIN"; + } + return wvcdm::UnknownEnumValueToString(static_cast(method)); } -string FormatAttributes(const string& indent, const Attributes& attributes) { - string result; +void FormatOEMCryptoResult(int oemcrypto_result, size_t parent_indent, + std::ostream& os) { + const size_t indent = parent_indent + kIndentIncrement; + FormatIndent(indent, os); + os << "value: " << oemcrypto_result << '\n'; + FormatIndent(indent, os); + os << "name: "; + FormatString(::wvcdm::OemCryptoResultToString( + static_cast(oemcrypto_result)), + os); + os << '\n'; +} + +void FormatCdmErrorCode(int error_code, size_t parent_indent, + std::ostream& os) { + const size_t indent = parent_indent + kIndentIncrement; + FormatIndent(indent, os); + os << "value: " << error_code << '\n'; + FormatIndent(indent, os); + os << "name: "; + FormatString(::wvcdm::CdmResponseEnumToString( + static_cast(error_code)), + os); + os << '\n'; +} + +void FormatAttributes(const Attributes& attributes, size_t parent_indent, + std::ostream& os) { + const size_t indent = parent_indent + kIndentIncrement; + bool has_value = false; if (attributes.has_error_code()) { - result.assign(indent); - result.append("error_code:\n"); - string next_indent = indent + kIndentSpaces; - result.append(next_indent + "name: "); - result.append(wvcdm::CdmResponseEnumToString( - wvcdm::CdmResponseEnum(attributes.error_code()))); - result.append("\n" + next_indent + "value: "); - result.append(to_string(attributes.error_code())); + has_value = true; + FormatIndent(indent, os); + os << "error_code:\n"; + FormatCdmErrorCode(attributes.error_code(), indent, os); } if (attributes.has_error_code_bool()) { - result.assign(indent); - result.append("success: "); - result.append(attributes.error_code_bool() ? "true" : "false"); + has_value = true; + FormatIndent(indent, os); + os << "success: "; + os << (attributes.error_code_bool() ? "true" : "false"); + os << '\n'; } if (attributes.has_cdm_security_level()) { - result.assign(indent); - result.append("cdm_security_level: "); - result.append(to_string(attributes.cdm_security_level())); + has_value = true; + FormatIndent(indent, os); + const wvcdm::CdmSecurityLevel cdm_security_level = + static_cast(attributes.cdm_security_level()); + os << "cdm_security_level: "; + FormatString(wvcdm::CdmSecurityLevelToString(cdm_security_level), os); + os << '\n'; } if (attributes.has_security_level()) { - result.assign(indent); - result.append("security_level: "); - result.append(to_string(attributes.security_level())); + has_value = true; + FormatIndent(indent, os); + const wvcdm::RequestedSecurityLevel security_level = + static_cast(attributes.security_level()); + os << "security_level: "; + FormatString(wvcdm::RequestedSecurityLevelToString(security_level), os); + os << '\n'; } if (attributes.has_length()) { - result.assign(indent); - result.append("length: "); - result.append(to_string(attributes.length())); + has_value = true; + FormatIndent(indent, os); + os << "length: " << attributes.length() << '\n'; } if (attributes.has_encryption_algorithm()) { - result.assign(indent); - result.append("encryption_algorithm: "); - result.append(to_string(attributes.encryption_algorithm())); + has_value = true; + FormatIndent(indent, os); + // TODO(sigquit): Create a formatter for CdmEncryptionAlgorithm. + os << "encryption_algorithm: " << attributes.encryption_algorithm() << '\n'; } if (attributes.has_signing_algorithm()) { - result.assign(indent); - result.append("signing_algorithm: "); - result.append(to_string(attributes.signing_algorithm())); + has_value = true; + FormatIndent(indent, os); + // TODO(sigquit): Create a formatter for CdmSigningAlgorithm. + os << "signing_algorithm: " << attributes.signing_algorithm() << '\n'; } if (attributes.has_oem_crypto_result()) { - result.assign(indent); - result.append("oemcrypto_result:\n"); - string next_indent = indent + kIndentSpaces; - result.append(next_indent + "name: "); - result.append(::wvcdm::OemCryptoResultToString( - static_cast(attributes.oem_crypto_result()))); - result.append("\n" + next_indent + "value: "); - result.append(to_string(attributes.oem_crypto_result())); + has_value = true; + FormatIndent(indent, os); + os << "oemcrypto_result:\n"; + FormatOEMCryptoResult(attributes.oem_crypto_result(), indent, os); } if (attributes.has_key_status_type()) { - result.assign(indent); - result.append("key_status_type: "); - result.append(to_string(attributes.key_status_type())); + has_value = true; + FormatIndent(indent, os); + os << "key_status_type: " << attributes.key_status_type() << '\n'; } if (attributes.has_event_type()) { - result.assign(indent); - result.append("event_type: "); - result.append(to_string(attributes.event_type())); + has_value = true; + FormatIndent(indent, os); + os << "event_type: " << attributes.event_type() << '\n'; } if (attributes.has_key_request_type()) { - result.assign(indent); - result.append("key_request_type: "); - result.append(to_string(attributes.key_request_type())); + has_value = true; + FormatIndent(indent, os); + // TODO(sigquit): Create a formatter for CdmKeyRequestType. + os << "key_request_type: " << attributes.key_request_type() << '\n'; } if (attributes.has_license_type()) { - result.assign(indent); - result.append("license_type: "); - result.append(to_string(attributes.license_type())); + has_value = true; + FormatIndent(indent, os); + const wvcdm::CdmLicenseType license_type = + static_cast(attributes.license_type()); + os << "license_type: "; + FormatString(wvcdm::CdmLicenseTypeToString(license_type), os); + os << '\n'; } if (attributes.has_error_detail()) { - result.assign(indent); - result.append("error_detail: "); - result.append(to_string(attributes.error_detail())); + has_value = true; + FormatIndent(indent, os); + os << "error_detail: " << attributes.error_detail() << '\n'; } - if (result.size()) { - return result; - } else { - return " null"; + if (attributes.has_oem_crypto_signature_hash_algorithm()) { + has_value = true; + FormatIndent(indent, os); + // TODO(sigquit): Create a formatter for OEMCrypto_SignatureHashAlgorithm. + os << "oem_crypto_signature_hash_algorithm: "; + os << attributes.oem_crypto_signature_hash_algorithm() << '\n'; + } + + if (!has_value) { + FormatIndent(indent, os); + os << "value: null\n"; } } -string FormatCounterMetric(const string& indent, const CounterMetric& cm) { - string result(indent + kIndentSpaces); +void FormatCounterMetricItem(const CounterMetric& cm, size_t parent_indent, + int item_index, std::ostream& os) { + const size_t indent = parent_indent + kIndentIncrement; + FormatListIndent(parent_indent, os); if (cm.has_count()) { - result.append(string("count: ") + to_string(cm.count()) + "\n"); - if (cm.has_attributes()) { - string newIndent(indent + kIndentSpaces); - result.append(FormatAttributes(newIndent, cm.attributes())); - } + os << "count: " << cm.count(); + } else { + os << "count: null"; + } + os << " # [" << item_index << "]\n"; + + if (cm.has_attributes()) { + FormatIndent(indent, os); + os << "attributes:\n"; + FormatAttributes(cm.attributes(), indent, os); } - return result; } -string FormatDistributionMetric(const string& indent, const DistributionMetric& dm) { - string result; +void FormatDistributionMetricItem(const DistributionMetric& dm, + size_t parent_indent, int item_index, + std::ostream& os) { + const size_t indent = parent_indent + kIndentIncrement; + FormatListIndent(parent_indent, os); + os << "min: "; if (dm.has_min()) { - std::ostringstream buffer; - buffer << dm.min(); - result.append(indent + kIndentSpaces); - result.append(string("min: ") + buffer.str() + "\n"); + os << dm.min() << " # [" << item_index << "]\n"; + } else { + os << "null # [" << item_index << "]\n"; } + + FormatIndent(indent, os); + os << "max: "; if (dm.has_max()) { - std::ostringstream buffer; - buffer << dm.max(); - result.append(indent + kIndentSpaces); - result.append(string("max: ") + buffer.str() + "\n"); + os << dm.max() << '\n'; + } else { + os << "null\n"; } + + FormatIndent(indent, os); + os << "mean: "; if (dm.has_mean()) { - std::ostringstream buffer; - buffer << dm.mean(); - result.append(indent + kIndentSpaces); - result.append(string("mean: ") + buffer.str() + "\n"); + os << dm.mean() << '\n'; + } else { + os << "null\n"; } + + FormatIndent(indent, os); + os << "variance: "; if (dm.has_variance()) { - std::ostringstream buffer; - buffer << dm.variance(); - result.append(indent + kIndentSpaces); - result.append(string("variance: ") + buffer.str() + "\n"); + os << dm.variance() << '\n'; + } else { + os << "null\n"; } + + FormatIndent(indent, os); + os << "count: "; if (dm.has_operation_count()) { - result.append(indent + kIndentSpaces); - result.append(string("count: ") + to_string(dm.operation_count()) + "\n"); + os << dm.operation_count() << '\n'; + } else { + os << "null\n"; } + if (dm.has_attributes()) { - string newIndent(indent + kIndentSpaces); - result.append(FormatAttributes(newIndent, dm.attributes())); + FormatIndent(indent, os); + os << "attributes:\n"; + FormatAttributes(dm.attributes(), indent, os); } - return result; } -string FormatValueMetric(const ValueMetric& vm) { - string result; +void FormatValueMetric(const ValueMetric& vm, size_t parent_indent, + std::ostream& os) { + const size_t indent = parent_indent + kIndentIncrement; + bool has_value = false; + // For value metrics, only one of their fields should be set; however, + // this is not properly enforced by the protobuf, and all fields must be + // checked. if (vm.has_error_code()) { - result.append("error_code:\n"); - result.append(kIndentSpaces + "name: "); - result.append(DrmHalStatusToString(static_cast(vm.error_code()))); - result.append("\n" + kIndentSpaces + "value: "); - result.append(to_string(vm.error_code())); + has_value = true; + FormatIndent(indent, os); + os << "error_code:\n"; + // Nearly every error code set for a value metric is OEMCryptoResult. + FormatOEMCryptoResult(vm.error_code(), indent, os); } + if (vm.has_int_value()) { - result.append(to_string(vm.int_value())); + has_value = true; + FormatIndent(indent, os); + os << "int_value: " << vm.int_value() << '\n'; } + if (vm.has_double_value()) { - std::ostringstream buffer; - buffer << vm.double_value(); - result.append(buffer.str()); + has_value = true; + FormatIndent(indent, os); + os << "double_value: " << vm.double_value() << '\n'; } + if (vm.has_string_value()) { - result.append("\"" + vm.string_value() + "\""); + has_value = true; + FormatIndent(indent, os); + os << "string_value: "; + FormatString(vm.string_value(), os); + os << '\n'; + } + + if (!has_value) { + // Can't be empty, need to provide some field. + FormatIndent(indent, os); + os << "value: null\n"; } - return result; } -#define FORMAT_REPEATED_DISTRIBUTION(NAME, INDENT) \ - if (metrics.NAME##_size() == 1) { \ - result.append(INDENT + #NAME + ":\n"); \ - result.append(FormatDistributionMetric(INDENT, metrics.NAME(0)) + "\n"); \ - } else { \ - for (int i = 0; i < metrics.NAME##_size(); i++) { \ - result.append(INDENT + #NAME "[" + to_string(i) + "]:\n"); \ - result.append(FormatDistributionMetric(INDENT, metrics.NAME(i)) + "\n"); \ - } \ +#define FORMAT_REPEATED_DISTRIBUTION(NAME, INDENT) \ + if (metrics.NAME##_size() > 0) { \ + FormatIndent(INDENT, os); \ + os << #NAME ": # count = " << metrics.NAME##_size() << '\n'; \ + for (int i = 0; i < metrics.NAME##_size(); i++) { \ + FormatDistributionMetricItem(metrics.NAME(i), INDENT, i, os); \ + } \ } #define FORMAT_REPEATED_COUNTER(NAME, INDENT) \ - if (metrics.NAME##_size() == 1) { \ - result.append(INDENT + #NAME ":\n"); \ - result.append(FormatCounterMetric(INDENT, metrics.NAME(0)) + "\n"); \ - } else { \ + if (metrics.NAME##_size() > 0) { \ + FormatIndent(INDENT, os); \ + os << #NAME ": # count = " << metrics.NAME##_size() << '\n'; \ for (int i = 0; i < metrics.NAME##_size(); i++) { \ - result.append(INDENT + #NAME "[" + to_string(i) + "]: [\n"); \ - result.append(FormatCounterMetric(INDENT, metrics.NAME(i)) + "\n"); \ - result.append("]\n"); \ + FormatCounterMetricItem(metrics.NAME(i), INDENT, i, os); \ } \ } -#define FORMAT_OPTIONAL_VALUE(NAME, INDENT) \ +#define FORMAT_OPTIONAL_VALUE(NAME, INDENT) \ + if (metrics.has_##NAME()) { \ + FormatIndent(INDENT, os); \ + os << #NAME ":\n"; \ + FormatValueMetric(metrics.NAME(), INDENT, os); \ + } + +#define FORMAT_OPTIONAL_INITIALIZATION_MODE(NAME, INDENT) \ + if (metrics.has_##NAME()) { \ + FormatIndent(INDENT, os); \ + os << #NAME ": "; \ + const OEMCryptoInitializationMode mode = \ + static_cast(metrics.NAME().int_value()); \ + FormatString(OEMCryptoInitializeModeToString(mode), os); \ + os << '\n'; \ + } + +#define FORMAT_OPTIONAL_HDCP_CAPABILITY(NAME, INDENT) \ if (metrics.has_##NAME()) { \ - result.append(INDENT + #NAME ": " + FormatValueMetric(metrics.NAME()) + \ - "\n"); \ + FormatIndent(INDENT, os); \ + os << #NAME ": "; \ + const OEMCrypto_HDCP_Capability hdcp_level = \ + static_cast(metrics.NAME().int_value()); \ + FormatString(OEMCryptoHdcpCapabilityToString(hdcp_level), os); \ + os << '\n'; \ } -#define FORMAT_OPTIONAL_INITIALIZATION_MODE(NAME, INDENT) \ - if (metrics.has_##NAME()) { \ - result.append(INDENT + #NAME ": "); \ - result.append(FormatOEMCryptoInitializeMode(metrics.NAME()) + "\n"); \ +#define FORMAT_OPTIONAL_PROVISIONING_METHOD(NAME, INDENT) \ + if (metrics.has_##NAME()) { \ + FormatIndent(INDENT, os); \ + os << #NAME ": "; \ + const OEMCrypto_ProvisioningMethod method = \ + static_cast(metrics.NAME().int_value()); \ + FormatString(OEMCryptoProvisioningMethodToString(method), os); \ + os << '\n'; \ } -#define FORMAT_OPTIONAL_HDCP_CAPABILITY(NAME, INDENT) \ - if (metrics.has_##NAME()) { \ - result.append(INDENT + #NAME ": "); \ - result.append(FormatOEMCryptoHdcpCapability(metrics.NAME()) + "\n"); \ - } - -#define FORMAT_OPTIONAL_PROVISIONING_METHOD(NAME, INDENT) \ - if (metrics.has_##NAME()) { \ - result.append(INDENT + #NAME ": "); \ - result.append(FormatOEMCryptoProvisioningMethod(metrics.NAME()) + "\n"); \ - } - -#define FORMAT_OPTIONAL_CRYPTO_METRICS(NAME, INDENT) \ - if (metrics.has_##NAME()) { \ - FormatCryptoMetrics(metrics.NAME(), INDENT, result); \ +#define FORMAT_OPTIONAL_CRYPTO_METRICS(NAME, INDENT) \ + if (metrics.has_##NAME()) { \ + FormatIndent(INDENT, os); \ + os << #NAME ":\n"; \ + FormatCryptoMetrics(metrics.NAME(), INDENT, os); \ } void FormatCryptoMetrics(const WvCdmMetrics_CryptoMetrics metrics, - const string& indent, string& result) { + size_t parent_indent, std::ostream& os) { + const size_t indent = parent_indent + kIndentIncrement; // Crypto Session Metrics. FORMAT_OPTIONAL_VALUE(crypto_session_security_level, indent); FORMAT_REPEATED_COUNTER(crypto_session_delete_all_usage_reports, indent); @@ -401,7 +497,7 @@ void FormatCryptoMetrics(const WvCdmMetrics_CryptoMetrics metrics, // CdmUsageEntryStorageType in wv_cdm_types.h. FORMAT_OPTIONAL_VALUE(usage_table_header_lru_evicted_entry_type, indent); - // OemCrypto metrics. + // OEMCrypto metrics. FORMAT_OPTIONAL_VALUE(oemcrypto_api_version, indent); FORMAT_REPEATED_COUNTER(oemcrypto_close_session, indent); FORMAT_REPEATED_DISTRIBUTION(oemcrypto_copy_buffer_time_us, indent); @@ -458,7 +554,7 @@ void FormatCryptoMetrics(const WvCdmMetrics_CryptoMetrics metrics, FORMAT_REPEATED_COUNTER(oemcrypto_set_decrypt_hash, indent); FORMAT_OPTIONAL_VALUE(oemcrypto_resource_rating_tier, indent); - // Oemcrypto V16 metrics below + // OEMCrypto V16 metrics below FORMAT_REPEATED_DISTRIBUTION(oemcrypto_prep_and_sign_license_request_time_us, indent); FORMAT_REPEATED_DISTRIBUTION(oemcrypto_prep_and_sign_renewal_request_time_us, @@ -475,8 +571,21 @@ void FormatCryptoMetrics(const WvCdmMetrics_CryptoMetrics metrics, FORMAT_OPTIONAL_VALUE(oemcrypto_watermarking_support, indent); } -void FormatSessionMetrics(const WvCdmMetrics_SessionMetrics& metrics, - const string& indent, string& result) { +void FormatSessionMetricsItem(const WvCdmMetrics_SessionMetrics& metrics, + size_t parent_indent, int item_index, + std::ostream& os) { + const size_t indent = parent_indent + kIndentIncrement; + // Session ID needs special formatting because the index + // of this element must be commented on the first line. + FormatListIndent(parent_indent, os); + os << "session_id:"; + if (metrics.has_session_id()) { + os << " # [" << item_index << "]\n"; + FormatValueMetric(metrics.session_id(), indent, os); + } else { + os << " null # [" << item_index << "]\n"; + } + FORMAT_OPTIONAL_VALUE(session_id, indent); FORMAT_OPTIONAL_CRYPTO_METRICS(crypto_metrics, indent); FORMAT_OPTIONAL_VALUE(cdm_session_life_span_ms, indent); @@ -490,7 +599,9 @@ void FormatSessionMetrics(const WvCdmMetrics_SessionMetrics& metrics, } void FormatEngineMetrics(const WvCdmMetrics_EngineMetrics& metrics, - const string& indent, string& result) { + size_t parent_indent, std::ostream& os) { + const size_t indent = parent_indent + kIndentIncrement; + FORMAT_OPTIONAL_CRYPTO_METRICS(crypto_metrics, indent); // OEMCrypto Initialize Metrics. @@ -530,25 +641,27 @@ void FormatEngineMetrics(const WvCdmMetrics_EngineMetrics& metrics, } // anonymous namespace namespace wv_metrics { -void FormatWvCdmMetrics(const WvCdmMetrics& metrics, string& result) { - string indent = kIndentSpaces + kIndentSpaces + kIndentSpaces; - string next_indent = indent + kIndentSpaces; - - result.append(kIndentSpaces + " engine_metrics:"); +void FormatWvCdmMetrics(const WvCdmMetrics& metrics, size_t parent_indent, + std::ostream& os) { + const size_t indent = parent_indent + kIndentIncrement; + FormatIndent(indent, os); + os << "engine_metrics:"; if (metrics.has_engine_metrics()) { - result.append("\n"); - FormatEngineMetrics(metrics.engine_metrics(), indent, result); + os << '\n'; + FormatEngineMetrics(metrics.engine_metrics(), indent, os); } else { - result.append(" null\n"); + os << "null\n"; } - result.append(kIndentSpaces + " session_metrics:"); - if (metrics.session_metrics_size()) { + + FormatIndent(indent, os); + os << "session_metrics:"; + if (metrics.session_metrics_size() > 0) { + os << " # count = " << metrics.session_metrics_size() << '\n'; for (int i = 0; i < metrics.session_metrics_size(); i++) { - result.append("\n" + indent + "session[" + to_string(i) + "]:\n"); - FormatSessionMetrics(metrics.session_metrics(i), next_indent, result); + FormatSessionMetricsItem(metrics.session_metrics(i), indent, i, os); } } else { - result.append(" null\n"); + os << " []\n"; } } } // namespace wv_metrics