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 c84b9afd38)
This commit is contained in:
Alex Dale
2023-07-12 16:37:09 -07:00
committed by Rahul Frias
parent d655ffbfe7
commit 3ea91f35dd
4 changed files with 113 additions and 6 deletions

View File

@@ -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

View File

@@ -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);

View File

@@ -93,6 +93,8 @@ constexpr size_t kAes128BlockSize = 16;
constexpr int kMaxTerminateCountDown = 5;
constexpr char kUnavailableHdcpLevel[] = "<unavailable>";
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<int>(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(&current_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(&current_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, "

View File

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