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