Source release v3.2.0
This commit is contained in:
182
cdm/src/cdm.cpp
182
cdm/src/cdm.cpp
@@ -17,6 +17,7 @@
|
||||
#include "license.h"
|
||||
#include "log.h"
|
||||
#include "properties.h"
|
||||
#include "string_conversions.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
#include "wv_cdm_event_listener.h"
|
||||
|
||||
@@ -114,7 +115,16 @@ class CdmImpl : public Cdm,
|
||||
virtual ~CdmImpl();
|
||||
|
||||
// Cdm:
|
||||
virtual Status setServerCertificate(const std::string& certificate) OVERRIDE;
|
||||
virtual Status setServiceCertificate(const std::string& certificate) OVERRIDE;
|
||||
|
||||
virtual bool isProvisioned() OVERRIDE;
|
||||
|
||||
virtual Status removeProvisioning() OVERRIDE;
|
||||
|
||||
virtual Status removeUsageTable() OVERRIDE;
|
||||
|
||||
virtual Status listStoredLicenses(
|
||||
std::vector<std::string>* key_set_ids) OVERRIDE;
|
||||
|
||||
virtual Status createSession(SessionType session_type,
|
||||
std::string* session_id) OVERRIDE;
|
||||
@@ -177,6 +187,8 @@ class CdmImpl : public Cdm,
|
||||
const std::string& key_id, GenericSigningAlgorithmType algorithm,
|
||||
const std::string& signature) OVERRIDE;
|
||||
|
||||
virtual Status setVideoResolution(const std::string& session_id,
|
||||
uint32_t width, uint32_t height) OVERRIDE;
|
||||
// ITimerClient:
|
||||
virtual void onTimerExpired(void* context) OVERRIDE;
|
||||
|
||||
@@ -251,29 +263,55 @@ CdmImpl::~CdmImpl() {
|
||||
host.timer->cancel(this);
|
||||
}
|
||||
|
||||
Cdm::Status CdmImpl::setServerCertificate(const std::string& certificate) {
|
||||
if (!property_set_.use_privacy_mode()) {
|
||||
LOGE("Cannot set server certificate if privacy mode is disabled.");
|
||||
return kNotSupported;
|
||||
}
|
||||
Cdm::Status CdmImpl::setServiceCertificate(const std::string& certificate) {
|
||||
|
||||
if (certificate.empty()) {
|
||||
LOGE("An empty server certificate is invalid.");
|
||||
return kTypeError;
|
||||
LOGW("An empty licensing service certificate may be invalid.");
|
||||
}
|
||||
|
||||
if (CdmLicense::VerifySignedServiceCertificate(certificate) != NO_ERROR) {
|
||||
LOGE("Invalid server certificate!");
|
||||
// Check for properly signed and well-formed certificate.
|
||||
// Keep results. NOTE: an empty certificate should be accepted here.
|
||||
CdmResponseType status = cdm_engine_.SetServiceCertificate(certificate);
|
||||
if (status != NO_ERROR) {
|
||||
LOGE("Invalid service certificate! Error code = %d", status);
|
||||
return kTypeError;
|
||||
}
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
property_set_.set_service_certificate(certificate);
|
||||
bool CdmImpl::isProvisioned() {
|
||||
return cdm_engine_.IsProvisioned(kSecurityLevelL1);
|
||||
}
|
||||
|
||||
Cdm::Status CdmImpl::removeProvisioning() {
|
||||
if (cdm_engine_.Unprovision(kSecurityLevelL1) != NO_ERROR) {
|
||||
return kUnexpectedError;
|
||||
}
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
Cdm::Status CdmImpl::removeUsageTable() {
|
||||
if (cdm_engine_.DeleteUsageTable(kSecurityLevelL1) != NO_ERROR) {
|
||||
return kUnexpectedError;
|
||||
}
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
Cdm::Status CdmImpl::listStoredLicenses(std::vector<std::string>* key_set_ids) {
|
||||
if (key_set_ids == NULL) {
|
||||
LOGE("Missing vector parameter to receive key_set_ids.");
|
||||
return kTypeError;
|
||||
}
|
||||
if (cdm_engine_.ListStoredLicenses(kSecurityLevelL1, key_set_ids) !=
|
||||
NO_ERROR) {
|
||||
return kUnexpectedError;
|
||||
}
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
Cdm::Status CdmImpl::createSession(SessionType session_type,
|
||||
std::string* session_id) {
|
||||
if (NULL == session_id) {
|
||||
if (session_id == NULL) {
|
||||
LOGE("Missing session ID pointer.");
|
||||
return kTypeError;
|
||||
}
|
||||
@@ -420,21 +458,8 @@ Cdm::Status CdmImpl::generateRequest(const std::string& session_id,
|
||||
sessions_[session_id].callable = true;
|
||||
assert(key_request.type == kKeyRequestTypeInitial);
|
||||
|
||||
MessageType message_type;
|
||||
if (property_set_.use_privacy_mode() &&
|
||||
property_set_.service_certificate().empty()) {
|
||||
// We can deduce that this is a server cert request, even though CdmEgine
|
||||
// cannot currently inform us of this.
|
||||
// Previously, we used message type kIndividiualizationRequest for this.
|
||||
// The EME editor has clarified that this was a misinterpretation of the
|
||||
// spec, and that this should also be kLicenseRequest.
|
||||
LOGI("A server certificate request has been generated.");
|
||||
message_type = kLicenseRequest;
|
||||
} else {
|
||||
LOGI("A license request has been generated.");
|
||||
message_type = kLicenseRequest;
|
||||
}
|
||||
listener_->onMessage(session_id, message_type, key_request.message);
|
||||
LOGI("A license request has been generated.");
|
||||
listener_->onMessage(session_id, kLicenseRequest, key_request.message);
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
@@ -449,8 +474,9 @@ Cdm::Status CdmImpl::load(const std::string& session_id) {
|
||||
return kQuotaExceeded;
|
||||
}
|
||||
|
||||
CdmResponseType result = cdm_engine_.OpenSession(
|
||||
"com.widevine.alpha", &property_set_, session_id, this);
|
||||
CdmResponseType result = cdm_engine_.OpenSession("com.widevine.alpha",
|
||||
&property_set_, session_id,
|
||||
this);
|
||||
switch (result) {
|
||||
case NO_ERROR:
|
||||
break;
|
||||
@@ -504,6 +530,13 @@ Cdm::Status CdmImpl::load(const std::string& session_id) {
|
||||
return kUnexpectedError;
|
||||
}
|
||||
|
||||
if (!policy_timer_enabled_) {
|
||||
policy_timer_enabled_ = true;
|
||||
host.timer->setTimeout(kPolicyTimerDurationMilliseconds,
|
||||
this,
|
||||
kPolicyTimerContext);
|
||||
}
|
||||
|
||||
sessions_[session_id].type = kPersistentLicense;
|
||||
sessions_[session_id].callable = true;
|
||||
return kSuccess;
|
||||
@@ -590,34 +623,10 @@ Cdm::Status CdmImpl::update(const std::string& session_id,
|
||||
CdmResponseType result =
|
||||
cdm_engine_.AddKey(session_id, response, &key_set_id);
|
||||
|
||||
if (result == NEED_KEY) {
|
||||
// We just provisioned a server certificate.
|
||||
assert(property_set_.use_privacy_mode());
|
||||
|
||||
// The cert is now available to all sessions in this CDM instance.
|
||||
// This is consistent with the behavior of the Chrome CDM.
|
||||
assert(!property_set_.service_certificate().empty());
|
||||
|
||||
// The underlying session in CdmEngine has stored a copy of the original
|
||||
// init data, so we can use an empty one this time.
|
||||
InitializationData empty_init_data;
|
||||
CdmKeyRequest key_request;
|
||||
|
||||
CdmResponseType result = cdm_engine_.GenerateKeyRequest(
|
||||
session_id, session_id, empty_init_data, kLicenseTypeDeferred,
|
||||
app_parameters_, &key_request);
|
||||
|
||||
if (result != KEY_MESSAGE) {
|
||||
LOGE("Unexpected error %d", result);
|
||||
return kUnexpectedError;
|
||||
}
|
||||
|
||||
LOGI("A deferred license request has been generated.");
|
||||
assert(key_request.type == kKeyRequestTypeInitial);
|
||||
MessageType message_type = kLicenseRequest;
|
||||
listener_->onMessage(session_id, message_type, key_request.message);
|
||||
return kSuccess;
|
||||
} else if (result == OFFLINE_LICENSE_PROHIBITED) {
|
||||
// result should only be NEED_KEY after server certificate provisioning, which
|
||||
// should no longer happen in this version of the CDM.
|
||||
assert(result != NEED_KEY);
|
||||
if (result == OFFLINE_LICENSE_PROHIBITED) {
|
||||
LOGE("A temporary session cannot be used for a persistent license.");
|
||||
return kRangeError;
|
||||
} else if (result == STORAGE_PROHIBITED) {
|
||||
@@ -986,6 +995,22 @@ Cdm::Status CdmImpl::genericVerify(
|
||||
return kUnexpectedError;
|
||||
}
|
||||
|
||||
Cdm::Status CdmImpl::setVideoResolution(const std::string& session_id,
|
||||
uint32_t width, uint32_t height) {
|
||||
// Verify that width * height will fit into a 32-bit quantity.
|
||||
// This is done to be compatible with the video resolution in the
|
||||
// license policy settings.
|
||||
uint64_t pixels = width;
|
||||
pixels *= height;
|
||||
if (pixels >= (1ULL << 32))
|
||||
return kRangeError;
|
||||
if (cdm_engine_.NotifyResolution(session_id, width, height)) {
|
||||
return kSuccess;
|
||||
} else {
|
||||
return kSessionNotFound;
|
||||
}
|
||||
}
|
||||
|
||||
void CdmImpl::onTimerExpired(void* context) {
|
||||
if (context == kPolicyTimerContext) {
|
||||
if (policy_timer_enabled_) {
|
||||
@@ -1127,16 +1152,20 @@ Cdm::Status Cdm::initialize(
|
||||
IClock* clock,
|
||||
ITimer* timer,
|
||||
LogLevel verbosity) {
|
||||
// If you want to direct-render on L3, CryptoSession will pass that request
|
||||
// along to OEMCrypto. But if you want to use an opaque handle on L3,
|
||||
// CryptoSession will silently ignore you and tell OEMCrypto to treat the
|
||||
// address as a clear buffer. :-(
|
||||
|
||||
// Specify the maximum severity of message that will be output to
|
||||
// the console. See core/include/log.h for the valid priority values.
|
||||
g_cutoff = static_cast<LogPriority>(verbosity);
|
||||
|
||||
// If you want to direct-render on L3, CryptoSession will pass that
|
||||
// request along to OEMCrypto. But if you want to use an opaque
|
||||
// handle on L3, CryptoSession will silently ignore you and tell
|
||||
// OEMCrypto to treat the address as a clear buffer.
|
||||
//
|
||||
// So this logic mirrors that in CryptoSession. Effectively, we are
|
||||
// detecting at init time the conditions that would prevent CryptoSession (in
|
||||
// its current form) from passing the desired buffer type constant to
|
||||
// OEMCrypto.
|
||||
// TODO: Discuss changes to CryptoSession.
|
||||
// So this logic mirrors that in CryptoSession. Effectively, we
|
||||
// are detecting at init time the conditions that would prevent
|
||||
// CryptoSession (in its current form) from passing the desired
|
||||
// buffer type constant to OEMCrypto.
|
||||
switch (secure_output_type) {
|
||||
case kOpaqueHandle:
|
||||
// This output type requires an OEMCrypto that reports L1.
|
||||
@@ -1166,9 +1195,6 @@ Cdm::Status Cdm::initialize(
|
||||
return kTypeError;
|
||||
}
|
||||
|
||||
// Our enum values match those in core/include/log.h
|
||||
g_cutoff = static_cast<LogPriority>(verbosity);
|
||||
|
||||
PropertiesCE::SetSecureOutputType(secure_output_type);
|
||||
PropertiesCE::SetClientInfo(client_info);
|
||||
Properties::Init();
|
||||
@@ -1267,14 +1293,14 @@ class FileSystem::Impl {
|
||||
widevine::Cdm::IStorage* storage;
|
||||
};
|
||||
|
||||
FileSystem::FileSystem() : FileSystem("", NULL) {}
|
||||
FileSystem::FileSystem() : impl_(new Impl) {
|
||||
impl_->storage = host.storage;
|
||||
}
|
||||
|
||||
FileSystem::FileSystem(const std::string& origin, void* extra_data)
|
||||
: impl_(new Impl), origin_(origin) {
|
||||
if (extra_data)
|
||||
impl_->storage = (widevine::Cdm::IStorage*)extra_data;
|
||||
else
|
||||
impl_->storage = host.storage;
|
||||
assert(NULL != extra_data);
|
||||
impl_->storage = (widevine::Cdm::IStorage*)extra_data;
|
||||
}
|
||||
|
||||
FileSystem::~FileSystem() {
|
||||
@@ -1316,4 +1342,12 @@ ssize_t FileSystem::FileSize(const std::string& file_path) {
|
||||
return impl_->storage->size(file_path);
|
||||
}
|
||||
|
||||
bool FileSystem::List(const std::string&,
|
||||
std::vector<std::string>* file_names) {
|
||||
if (!impl_ || !file_names)
|
||||
return false;
|
||||
|
||||
return impl_->storage->list(file_names);
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
@@ -143,4 +143,9 @@ bool Properties::AlwaysUseKeySetIds() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool Properties::UseProviderIdInProvisioningRequest() {
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
Reference in New Issue
Block a user