Source release 14.0.0

This commit is contained in:
John W. Bruce
2018-05-16 17:35:40 -07:00
parent 31381a1311
commit 3ab70cec4e
2053 changed files with 1585838 additions and 4614 deletions

View File

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

View File

@@ -85,7 +85,6 @@ void Properties::Init() {
oem_crypto_use_fifo_ = use_fifo_;
oem_crypto_use_userspace_buffers_ = use_userspace_buffers_;
provisioning_messages_are_binary_ = set_provisioning_messages_to_binary_;
security_level_path_backward_compatibility_support_ = false;
allow_service_certificate_requests_ = false;
session_property_set_.reset(new CdmClientPropertySetMap());
}