Source release 14.0.0
This commit is contained in:
174
cdm/src/cdm.cpp
174
cdm/src/cdm.cpp
@@ -138,6 +138,9 @@ class CdmImpl : public Cdm,
|
||||
|
||||
virtual Status deleteAllUsageRecords() OVERRIDE;
|
||||
|
||||
virtual Status getStatusForHdcpVersion(HdcpVersion hdcp,
|
||||
KeyStatus* key_status) OVERRIDE;
|
||||
|
||||
virtual Status createSession(SessionType session_type,
|
||||
std::string* session_id) OVERRIDE;
|
||||
|
||||
@@ -220,11 +223,13 @@ class CdmImpl : public Cdm,
|
||||
|
||||
private:
|
||||
KeyAllowedUsageFlags KeyAllowedFlags(const CdmKeyAllowedUsage& usages);
|
||||
bool SendProvisioningRequest(const std::string& session_id);
|
||||
Cdm::Status SendProvisioningRequest(const std::string& session_id);
|
||||
CdmEncryptionAlgorithm ConvertEncryptionAlgorithm(
|
||||
GenericEncryptionAlgorithmType algorithm);
|
||||
CdmSigningAlgorithm ConvertSigningAlgorithm(
|
||||
GenericSigningAlgorithmType algorithm);
|
||||
Cdm::Status ConvertHdcpLevel(const std::string& query_value,
|
||||
Cdm::HdcpVersion* result);
|
||||
|
||||
IEventListener* listener_;
|
||||
bool policy_timer_enabled_;
|
||||
@@ -251,12 +256,14 @@ class CdmImpl : public Cdm,
|
||||
InitDataType init_data_type;
|
||||
bool has_init_data;
|
||||
bool is_load;
|
||||
bool quota_overrun;
|
||||
|
||||
UnprovisionedSessionMetadata()
|
||||
: type((SessionType)-1),
|
||||
init_data_type((InitDataType)-1),
|
||||
has_init_data(false),
|
||||
is_load(false) {}
|
||||
is_load(false),
|
||||
quota_overrun(false) {}
|
||||
};
|
||||
typedef std::map<std::string, UnprovisionedSessionMetadata> UnprovisionedMap;
|
||||
|
||||
@@ -279,6 +286,8 @@ CdmImpl::~CdmImpl() {
|
||||
host.timer->cancel(this);
|
||||
}
|
||||
|
||||
// TODO(gmorgan): Add separate interfaces to set license/provisioning
|
||||
// service certificates. Right now this sets both.
|
||||
Cdm::Status CdmImpl::setServiceCertificate(const std::string& certificate) {
|
||||
|
||||
if (certificate.empty()) {
|
||||
@@ -287,14 +296,12 @@ Cdm::Status CdmImpl::setServiceCertificate(const std::string& certificate) {
|
||||
}
|
||||
|
||||
// Verify that the certificate is properly signed and well-formed.
|
||||
ServiceCertificate service_certificate;
|
||||
CdmResponseType status = service_certificate.Init(certificate);
|
||||
CdmResponseType status =
|
||||
cdm_engine_.SetProvisioningServiceCertificate(certificate);
|
||||
if (status != NO_ERROR) {
|
||||
LOGE("Invalid service certificate! Error code = %d", status);
|
||||
return kTypeError;
|
||||
}
|
||||
// TODO(gmorgan): remove when provisioning service certificate is added
|
||||
cdm_engine_.SetServiceCertificate(certificate);
|
||||
property_set_.set_service_certificate(certificate);
|
||||
return kSuccess;
|
||||
}
|
||||
@@ -379,13 +386,38 @@ Cdm::Status CdmImpl::deleteUsageRecord(const std::string& key_set_id) {
|
||||
}
|
||||
|
||||
Cdm::Status CdmImpl::deleteAllUsageRecords() {
|
||||
if (cdm_engine_.ReleaseAllUsageInfo(
|
||||
if (cdm_engine_.RemoveAllUsageInfo(
|
||||
property_set_.app_id(), kSecurityLevelL1) != NO_ERROR) {
|
||||
return kUnexpectedError;
|
||||
}
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
Cdm::Status CdmImpl::getStatusForHdcpVersion(Cdm::HdcpVersion hdcp,
|
||||
Cdm::KeyStatus* key_status) {
|
||||
std::string query_value;
|
||||
if (cdm_engine_.QueryStatus(kLevelDefault,
|
||||
QUERY_KEY_MAX_HDCP_LEVEL,
|
||||
&query_value) != NO_ERROR) {
|
||||
return kUnexpectedError;
|
||||
}
|
||||
|
||||
if (query_value == QUERY_VALUE_HDCP_NONE ||
|
||||
query_value == QUERY_VALUE_HDCP_LEVEL_UNKNOWN) {
|
||||
*key_status = Cdm::kOutputRestricted;
|
||||
} else if (query_value == QUERY_VALUE_HDCP_NO_DIGITAL_OUTPUT) {
|
||||
*key_status = Cdm::kUsable;
|
||||
} else {
|
||||
Cdm::HdcpVersion max_hdcp;
|
||||
if (ConvertHdcpLevel(query_value, &max_hdcp) != kSuccess) {
|
||||
return kUnexpectedError;
|
||||
}
|
||||
|
||||
*key_status = (hdcp <= max_hdcp ? Cdm::kUsable : Cdm::kOutputRestricted);
|
||||
}
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
Cdm::Status CdmImpl::createSession(SessionType session_type,
|
||||
std::string* session_id) {
|
||||
if (session_id == NULL) {
|
||||
@@ -449,20 +481,26 @@ Cdm::Status CdmImpl::generateRequest(const std::string& session_id,
|
||||
// We are not provisioned yet, save the init data and send a provisioning
|
||||
// request if needed.
|
||||
if (unprovisioned_sessions_[session_id].has_init_data) {
|
||||
LOGE("Request already generated: %s", session_id.c_str());
|
||||
return kInvalidState;
|
||||
if (!unprovisioned_sessions_[session_id].quota_overrun) {
|
||||
LOGE("Request already generated: %s", session_id.c_str());
|
||||
return kInvalidState;
|
||||
}
|
||||
} else {
|
||||
unprovisioned_sessions_[session_id].has_init_data = true;
|
||||
unprovisioned_sessions_[session_id].init_data_type = init_data_type;
|
||||
unprovisioned_sessions_[session_id].init_data = init_data;
|
||||
}
|
||||
|
||||
unprovisioned_sessions_[session_id].has_init_data = true;
|
||||
unprovisioned_sessions_[session_id].init_data_type = init_data_type;
|
||||
unprovisioned_sessions_[session_id].init_data = init_data;
|
||||
|
||||
if (provision_request_sent_)
|
||||
return kDeferred;
|
||||
|
||||
if (!SendProvisioningRequest(session_id))
|
||||
return kUnexpectedError;
|
||||
return kSuccess;
|
||||
Cdm::Status status = SendProvisioningRequest(session_id);
|
||||
if (status == kQuotaExceeded) {
|
||||
unprovisioned_sessions_[session_id].quota_overrun = true;
|
||||
} else {
|
||||
unprovisioned_sessions_[session_id].quota_overrun = false;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -512,7 +550,13 @@ Cdm::Status CdmImpl::generateRequest(const std::string& session_id,
|
||||
return kTypeError;
|
||||
}
|
||||
|
||||
InitializationData init_data_obj(init_data_type_name, init_data);
|
||||
std::string oec_version;
|
||||
if (cdm_engine_.QueryStatus(wvcdm::kLevelDefault,
|
||||
QUERY_KEY_OEMCRYPTO_API_VERSION,
|
||||
&oec_version) != NO_ERROR) {
|
||||
return kUnexpectedError;
|
||||
}
|
||||
InitializationData init_data_obj(init_data_type_name, init_data, oec_version);
|
||||
if (init_data_obj.IsEmpty()) {
|
||||
// Note that InitializationData's idea of "empty" includes "failed to find
|
||||
// and parse a Widevine PSSH". This should not happen for WebM init data,
|
||||
@@ -527,6 +571,11 @@ Cdm::Status CdmImpl::generateRequest(const std::string& session_id,
|
||||
session_id, session_id, init_data_obj,
|
||||
license_type, app_parameters_, &key_request);
|
||||
|
||||
if (result == LICENSE_REQUEST_NONCE_GENERATION_ERROR) {
|
||||
LOGE("Nonce quota exceeded");
|
||||
return kQuotaExceeded;
|
||||
}
|
||||
|
||||
if (result != KEY_MESSAGE) {
|
||||
LOGE("Unexpected error %d", result);
|
||||
return kUnexpectedError;
|
||||
@@ -564,10 +613,7 @@ Cdm::Status CdmImpl::load(const std::string& session_id) {
|
||||
|
||||
// Send a provisioning request right away. Then we will load the session
|
||||
// again in |update|.
|
||||
if (!SendProvisioningRequest(session_id))
|
||||
return kUnexpectedError;
|
||||
|
||||
return kSuccess;
|
||||
return SendProvisioningRequest(session_id);
|
||||
default:
|
||||
LOGE("Unexpected error %d", result);
|
||||
return kUnexpectedError;
|
||||
@@ -632,7 +678,14 @@ Cdm::Status CdmImpl::load(const std::string& session_id,
|
||||
return kTypeError;
|
||||
}
|
||||
|
||||
InitializationData init_data_obj(CENC_INIT_DATA_FORMAT, init_data);
|
||||
std::string oec_version;
|
||||
if (cdm_engine_.QueryStatus(wvcdm::kLevelDefault,
|
||||
QUERY_KEY_OEMCRYPTO_API_VERSION,
|
||||
&oec_version) != NO_ERROR) {
|
||||
return kUnexpectedError;
|
||||
}
|
||||
InitializationData init_data_obj(CENC_INIT_DATA_FORMAT, init_data,
|
||||
oec_version);
|
||||
if (init_data_obj.IsEmpty()) {
|
||||
// Note that InitializationData's idea of "empty" includes "failed to find
|
||||
// and parse a Widevine PSSH". This should not happen for WebM init data,
|
||||
@@ -643,9 +696,15 @@ Cdm::Status CdmImpl::load(const std::string& session_id,
|
||||
|
||||
CdmKeyRequest key_request;
|
||||
CdmResponseType result = cdm_engine_.GenerateKeyRequest(
|
||||
session_id, session_id, init_data_obj, kLicenseTypeSubSession,
|
||||
session_id, session_id, init_data_obj, kLicenseTypeEmbeddedKeyData,
|
||||
app_parameters_, &key_request);
|
||||
if (result != KEY_MESSAGE) {
|
||||
|
||||
if (result == LICENSE_REQUEST_NONCE_GENERATION_ERROR) {
|
||||
LOGE("Nonce quota exceeded");
|
||||
return kQuotaExceeded;
|
||||
}
|
||||
if (result != KEY_ADDED) {
|
||||
LOGE("Unexpected Failure: GenerateKeyRequest() returned %lu", result);
|
||||
return kUnexpectedError;
|
||||
}
|
||||
return kSuccess;
|
||||
@@ -664,8 +723,11 @@ Cdm::Status CdmImpl::update(const std::string& session_id,
|
||||
LOGE("Unexpected error %d", result);
|
||||
return kUnexpectedError;
|
||||
}
|
||||
}
|
||||
|
||||
// We are now provisioned, we need to recreate the unprovisioned sessions.
|
||||
// Check to see if any sessions have been waiting for provisioning.
|
||||
// If so, try to recreate those sessions and generate their requests.
|
||||
if (unprovisioned_sessions_.size() > 0) {
|
||||
Cdm::Status ret = kSuccess;
|
||||
for (UnprovisionedMap::iterator it = unprovisioned_sessions_.begin();
|
||||
it != unprovisioned_sessions_.end();) {
|
||||
@@ -679,8 +741,8 @@ Cdm::Status CdmImpl::update(const std::string& session_id,
|
||||
continue;
|
||||
}
|
||||
|
||||
result = cdm_engine_.OpenSession("com.widevine.alpha", &property_set_,
|
||||
it->first, this);
|
||||
CdmResponseType result = cdm_engine_.OpenSession(
|
||||
"com.widevine.alpha", &property_set_, it->first, this);
|
||||
if (result != NO_ERROR) {
|
||||
LOGE("Unexpected error %d", result);
|
||||
if (it->first == session_id)
|
||||
@@ -699,11 +761,16 @@ Cdm::Status CdmImpl::update(const std::string& session_id,
|
||||
if (it->second.has_init_data) {
|
||||
Cdm::Status generate_status = generateRequest(
|
||||
it->first, it->second.init_data_type, it->second.init_data);
|
||||
if (it->first != session_id)
|
||||
if (it->first != session_id) {
|
||||
listener_->onDeferredComplete(it->first, generate_status);
|
||||
else if (generate_status != kSuccess)
|
||||
} else if (generate_status == Cdm::kQuotaExceeded) {
|
||||
// Close session. We'll re-open it again on the next call to update.
|
||||
cdm_engine_.CloseSession(session_id);
|
||||
return Cdm::kQuotaExceeded;
|
||||
} else if (generate_status != kSuccess) {
|
||||
// Still erase the item because the session exists in |cdm_engine_|.
|
||||
ret = generate_status;
|
||||
}
|
||||
}
|
||||
unprovisioned_sessions_.erase(it++);
|
||||
}
|
||||
@@ -926,6 +993,11 @@ Cdm::Status CdmImpl::remove(const std::string& session_id) {
|
||||
CdmResponseType result = cdm_engine_.GenerateKeyRequest(
|
||||
session_id, session_id, empty_initialization_data,
|
||||
kLicenseTypeRelease, app_parameters_, &key_request);
|
||||
|
||||
if (result == LICENSE_REQUEST_NONCE_GENERATION_ERROR) {
|
||||
LOGE("Nonce quota exceeded");
|
||||
return kQuotaExceeded;
|
||||
}
|
||||
if (result != KEY_MESSAGE) {
|
||||
LOGE("Unexpected error %d", result);
|
||||
cdm_engine_.CloseSession(session_id);
|
||||
@@ -1142,6 +1214,13 @@ void CdmImpl::OnSessionRenewalNeeded(const CdmSessionId& session_id) {
|
||||
CdmKeyRequest key_request;
|
||||
CdmResponseType result =
|
||||
cdm_engine_.GenerateRenewalRequest(session_id, &key_request);
|
||||
if (result == LICENSE_RENEWAL_NONCE_GENERATION_ERROR) {
|
||||
// TODO(b/73606893): this error should be recoverable. Rather
|
||||
// than just giving up here, we should reset the event timer for
|
||||
// a second later and try again.
|
||||
LOGE("Nonce quota exceeded");
|
||||
return;
|
||||
}
|
||||
if (result != KEY_MESSAGE) {
|
||||
LOGE("Unexpected error %d", result);
|
||||
return;
|
||||
@@ -1154,7 +1233,7 @@ void CdmImpl::OnSessionRenewalNeeded(const CdmSessionId& session_id) {
|
||||
|
||||
void CdmImpl::OnSessionKeysChange(const CdmSessionId& session_id,
|
||||
const CdmKeyStatusMap& keys_status,
|
||||
bool /* has_new_usable_key */) {
|
||||
bool has_new_usable_key) {
|
||||
KeyStatusMap& map = sessions_[session_id].key_statuses;
|
||||
|
||||
CdmKeyStatusMap::const_iterator it;
|
||||
@@ -1189,7 +1268,7 @@ void CdmImpl::OnSessionKeysChange(const CdmSessionId& session_id,
|
||||
}
|
||||
}
|
||||
|
||||
listener_->onKeyStatusesChange(session_id);
|
||||
listener_->onKeyStatusesChange(session_id, has_new_usable_key);
|
||||
}
|
||||
|
||||
void CdmImpl::OnExpirationUpdate(const CdmSessionId& session_id,
|
||||
@@ -1214,7 +1293,7 @@ Cdm::KeyAllowedUsageFlags CdmImpl::KeyAllowedFlags(
|
||||
return flags;
|
||||
}
|
||||
|
||||
bool CdmImpl::SendProvisioningRequest(const std::string& session_id) {
|
||||
Cdm::Status CdmImpl::SendProvisioningRequest(const std::string& session_id) {
|
||||
// Generate a provisioning request.
|
||||
std::string empty_authority;
|
||||
std::string ignored_base_url;
|
||||
@@ -1222,14 +1301,17 @@ bool CdmImpl::SendProvisioningRequest(const std::string& session_id) {
|
||||
CdmResponseType result = cdm_engine_.GetProvisioningRequest(
|
||||
kCertificateWidevine, empty_authority, &signed_request,
|
||||
&ignored_base_url);
|
||||
if (result == CERT_PROVISIONING_NONCE_GENERATION_ERROR) {
|
||||
LOGE("Nonce quota exceeded");
|
||||
return kQuotaExceeded;
|
||||
}
|
||||
if (result != NO_ERROR) {
|
||||
LOGE("Unexpected error %d", result);
|
||||
return false;
|
||||
return kUnexpectedError;
|
||||
}
|
||||
listener_->onDirectIndividualizationRequest(session_id, signed_request);
|
||||
provision_request_sent_ = true;
|
||||
|
||||
return true;
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
CdmEncryptionAlgorithm CdmImpl::ConvertEncryptionAlgorithm(
|
||||
@@ -1252,6 +1334,22 @@ CdmSigningAlgorithm CdmImpl::ConvertSigningAlgorithm(
|
||||
}
|
||||
}
|
||||
|
||||
Cdm::Status CdmImpl::ConvertHdcpLevel(const std::string& query_value,
|
||||
Cdm::HdcpVersion* result) {
|
||||
if (query_value == QUERY_VALUE_HDCP_V1) {
|
||||
*result = kHdcp1_x;
|
||||
} else if (query_value == QUERY_VALUE_HDCP_V2_0) {
|
||||
*result = kHdcp2_0;
|
||||
} else if (query_value == QUERY_VALUE_HDCP_V2_1) {
|
||||
*result = kHdcp2_1;
|
||||
} else if (query_value == QUERY_VALUE_HDCP_V2_2) {
|
||||
*result = kHdcp2_2;
|
||||
} else {
|
||||
return kUnexpectedError;
|
||||
}
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
bool VerifyL1() {
|
||||
CryptoSession cs(NULL);
|
||||
return cs.GetSecurityLevel() == kSecurityLevelL1;
|
||||
@@ -1338,8 +1436,10 @@ Cdm* Cdm::create(IEventListener* listener,
|
||||
LOGE("No listener!");
|
||||
return NULL;
|
||||
}
|
||||
if (!storage)
|
||||
storage = host.storage;
|
||||
if (!storage) {
|
||||
LOGE("No storage!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return new CdmImpl(listener, storage, privacy_mode);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user