From 3ea91f35dd5707839db10be4ee224a0ff5637bd4 Mon Sep 17 00:00:00 2001 From: Alex Dale Date: Wed, 12 Jul 2023 16:37:09 -0700 Subject: [PATCH] Add HDCP cap logging on HDCP-based failures. [ Merge of http://go/wvgerrit/181152 ] [ Cherry-pick of http://ag/24137228 ] Partners have requested that we log HDCP information during certain operation: 1) Current and max HDCP capability when calls to decrypt or select key failure due to insufficient or mixed HDCP levels. 2) Current, desired and default HDCP level when video contraints are not met. To avoid spamming the logs, decrypt failures are only logged on their first occurrence, and unmet video constrains when one of the requirements change. Bug: 276686656 Bug: 292005982 Test: license_keys_unittest Test: Android WVTS on oriole Change-Id: I98b18e66d7ce1c474a018ae83af4f1c0b03308df (cherry picked from commit c84b9afd3814fbd5e4ec5eaecdcfd1f644fd21e7) --- .../cdm/core/include/crypto_session.h | 5 ++ .../cdm/core/include/license_key_status.h | 2 + .../cdm/core/src/crypto_session.cpp | 87 +++++++++++++++++-- .../cdm/core/src/license_key_status.cpp | 25 +++++- 4 files changed, 113 insertions(+), 6 deletions(-) diff --git a/libwvdrmengine/cdm/core/include/crypto_session.h b/libwvdrmengine/cdm/core/include/crypto_session.h index cc5e83bd..5d78f882 100644 --- a/libwvdrmengine/cdm/core/include/crypto_session.h +++ b/libwvdrmengine/cdm/core/include/crypto_session.h @@ -69,6 +69,8 @@ class CryptoSession { static CryptoSession* MakeCryptoSession( metrics::CryptoMetrics* crypto_metrics); + static const char* HdcpCapabilityToString(HdcpCapability hdcp_level); + virtual ~CryptoSession(); // This method will try to terminate OEMCrypto if |session_size_| is 0. @@ -542,6 +544,9 @@ class CryptoSession { // same error code in sequence of each other. A value of // OEMCrypto_SUCCESS indicates that no error have yet occurred. OEMCryptoResult last_decrypt_error_ = OEMCrypto_SUCCESS; + // Similar to |last_decrypt_error_|, but intended for calls to + // SelectKey(). + OEMCryptoResult last_select_key_error_ = OEMCrypto_SUCCESS; // In order to avoid creating a deadlock if instantiation needs to take any // of the CryptoSession static mutexes, |factory_| is protected by its own diff --git a/libwvdrmengine/cdm/core/include/license_key_status.h b/libwvdrmengine/cdm/core/include/license_key_status.h index d07cbffc..db9bd644 100644 --- a/libwvdrmengine/cdm/core/include/license_key_status.h +++ b/libwvdrmengine/cdm/core/include/license_key_status.h @@ -161,6 +161,8 @@ class LicenseKeyStatus { bool meets_security_level_constraints_ = true; CdmKeyAllowedUsage allowed_usage_; CryptoSession::HdcpCapability default_hdcp_level_ = HDCP_NONE; + CryptoSession::HdcpCapability last_reported_device_hdcp_level_ = HDCP_NONE; + CryptoSession::HdcpCapability last_reported_license_hdcp_level_ = HDCP_NONE; ConstraintList constraints_; CORE_DISALLOW_COPY_AND_ASSIGN(LicenseKeyStatus); diff --git a/libwvdrmengine/cdm/core/src/crypto_session.cpp b/libwvdrmengine/cdm/core/src/crypto_session.cpp index 70775e88..b9fe6703 100644 --- a/libwvdrmengine/cdm/core/src/crypto_session.cpp +++ b/libwvdrmengine/cdm/core/src/crypto_session.cpp @@ -93,6 +93,8 @@ constexpr size_t kAes128BlockSize = 16; constexpr int kMaxTerminateCountDown = 5; +constexpr char kUnavailableHdcpLevel[] = ""; + const std::string kStringNotAvailable = "NA"; // TODO(b/174412779): Remove when b/170704368 is fixed. @@ -292,6 +294,37 @@ OEMCryptoCipherMode ToOEMCryptoCipherMode(CdmCipherMode cipher_mode) { : OEMCrypto_CipherMode_CBCS; } +// static +const char* CryptoSession::HdcpCapabilityToString(HdcpCapability hdcp_level) { + switch (hdcp_level) { + case HDCP_NONE: + return "None"; + case HDCP_V1: // Means v1.x + return "v1.x"; + case HDCP_V2: // Means v2.0 + return "v2.0"; + case HDCP_V2_1: + return "v2.1"; + case HDCP_V2_2: + return "v2.2"; + case HDCP_V2_3: + return "v2.3"; + case HDCP_V1_0: + return "v1.0"; + case HDCP_V1_1: + return "v1.1"; + case HDCP_V1_2: + return "v1.2"; + case HDCP_V1_3: + return "v1.3"; + case HDCP_V1_4: + return "v1.4"; + case HDCP_NO_DIGITAL_OUTPUT: + return "No-Digital-Output"; + } + return UnknownEnumValueToString(static_cast(hdcp_level)); +} + CryptoSession::CryptoSession(metrics::CryptoMetrics* metrics) : metrics_(metrics), system_id_(NULL_SYSTEM_ID), @@ -1513,6 +1546,35 @@ CdmResponseType CryptoSession::SelectKey(const std::string& key_id, return key_session_->SelectKey(key_id, cipher_mode); }); + if (sts != last_select_key_error_) { + // Only log errors on first occurrence. Calls to SelectKey from + // the app are typically queued. If an error occurs, then it is + // expected that multiple failures will occur before the app is + // notified and stops queueing calls to the CDM. + last_select_key_error_ = sts; + if (sts == OEMCrypto_ERROR_INSUFFICIENT_HDCP || + sts == OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION) { + const char* status_message = OemCryptoResultToString(sts); + HdcpCapability current_hdcp_level; + HdcpCapability max_hdcp_level; + const CdmResponseType hdcp_status = + GetHdcpCapabilities(¤t_hdcp_level, &max_hdcp_level); + const char* current_hdcp_message = kUnavailableHdcpLevel; + const char* max_hdcp_message = kUnavailableHdcpLevel; + if (hdcp_status == NO_ERROR) { + current_hdcp_message = HdcpCapabilityToString(current_hdcp_level); + max_hdcp_message = HdcpCapabilityToString(max_hdcp_level); + } + LOGE( + "oec_session_id = %u, security_level = %s, " + "status = %s, current_hdcp_level = %s, " + "max_hdcp_level = %s", + oec_session_id_, + RequestedSecurityLevelToString(requested_security_level_), + status_message, current_hdcp_message, max_hdcp_message); + } + } + switch (sts) { // SelectKey errors. case OEMCrypto_SUCCESS: @@ -1790,11 +1852,26 @@ CdmResponseType CryptoSession::Decrypt( // the next call. The calling application may make several more // decrypt requests before the error is handled by the app. last_decrypt_error_ = sts; - if (sts == OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION) { - LOGW( - "OEMCrypto_DecryptCENC is warning of mixed HDCP output protection: " - "oec_session_id = %u", - oec_session_id_); + if (sts == OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION || + sts == OEMCrypto_ERROR_INSUFFICIENT_HDCP) { + const char* status_message = OemCryptoResultToString(sts); + HdcpCapability current_hdcp_level; + HdcpCapability max_hdcp_level; + const CdmResponseType hdcp_status = + GetHdcpCapabilities(¤t_hdcp_level, &max_hdcp_level); + const char* current_hdcp_message = kUnavailableHdcpLevel; + const char* max_hdcp_message = kUnavailableHdcpLevel; + if (hdcp_status == NO_ERROR) { + current_hdcp_message = HdcpCapabilityToString(current_hdcp_level); + max_hdcp_message = HdcpCapabilityToString(max_hdcp_level); + } + LOGE( + "OEMCrypto_DecryptCENC failed: oec_session_id = %u, " + "security_level = %s, status = %s, current_hdcp_level = %s, " + "max_hdcp_level = %s", + oec_session_id_, + RequestedSecurityLevelToString(requested_security_level_), + status_message, current_hdcp_message, max_hdcp_message); } else { LOGE( "OEMCrypto_DecryptCENC failed: oec_session_id = %u, " diff --git a/libwvdrmengine/cdm/core/src/license_key_status.cpp b/libwvdrmengine/cdm/core/src/license_key_status.cpp index 4e3bff28..7326be0d 100644 --- a/libwvdrmengine/cdm/core/src/license_key_status.cpp +++ b/libwvdrmengine/cdm/core/src/license_key_status.cpp @@ -316,6 +316,7 @@ void LicenseKeyStatus::ParseContentKey(const KeyContainer& key, if (key.has_required_protection()) { default_hdcp_level_ = ProtobufHdcpToOemCryptoHdcp(key.required_protection().hdcp()); + last_reported_license_hdcp_level_ = default_hdcp_level_; } } @@ -399,14 +400,36 @@ void LicenseKeyStatus::ApplyConstraints( } } + const bool device_hdcp_level_change = + (last_reported_device_hdcp_level_ != new_hdcp_level); + last_reported_device_hdcp_level_ = new_hdcp_level; + CryptoSession::HdcpCapability desired_hdcp_level = default_hdcp_level_; if (current_constraint && current_constraint->has_required_protection()) { desired_hdcp_level = ProtobufHdcpToOemCryptoHdcp( current_constraint->required_protection().hdcp()); } + const bool license_hdcp_level_change = + (desired_hdcp_level != last_reported_license_hdcp_level_); + last_reported_license_hdcp_level_ = desired_hdcp_level; - meets_constraints_ = + const bool meets_new_constrains = MeetsHdcpRequirements(desired_hdcp_level, new_hdcp_level); + const bool meets_constraints_change = + (meets_new_constrains != meets_constraints_); + meets_constraints_ = meets_new_constrains; + + const bool change = device_hdcp_level_change || license_hdcp_level_change || + meets_constraints_change; + if (!meets_constraints_ && change) { + LOGD( + "new_hdcp_level = %s, desired_hdcp_level = %s, " + "default_hdcp_level = %s, video_pixels = %u", + CryptoSession::HdcpCapabilityToString(new_hdcp_level), + CryptoSession::HdcpCapabilityToString(desired_hdcp_level), + CryptoSession::HdcpCapabilityToString(default_hdcp_level_), + video_pixels); + } } void LicenseKeyStatus::SetConstraints(const ConstraintList& constraints) {