Source release 15.3.0
This commit is contained in:
@@ -89,7 +89,8 @@ class CDM_EXPORT Cdm : public ITimerClient {
|
||||
kResourceContention = 107, // Recoverable
|
||||
kSessionStateLost = 108, // Recoverable
|
||||
kSystemStateLost = 109, // Recoverable
|
||||
kOutputTooLarge = 109, // Recoverable
|
||||
kOutputTooLarge = 110, // Recoverable
|
||||
kNeedsServiceCertificate = 111,
|
||||
|
||||
// This covers errors that we do not expect (see logs for details):
|
||||
kUnexpectedError = 99999,
|
||||
@@ -131,6 +132,7 @@ class CDM_EXPORT Cdm : public ITimerClient {
|
||||
kHdcp2_0 = 1,
|
||||
kHdcp2_1 = 2,
|
||||
kHdcp2_2 = 3,
|
||||
kHdcp2_3 = 4,
|
||||
} HdcpVersion;
|
||||
|
||||
// Permissible usages for a key. Returned as a set of flags; multiple
|
||||
@@ -343,6 +345,15 @@ class CDM_EXPORT Cdm : public ITimerClient {
|
||||
const ClientInfo& client_info, IStorage* storage,
|
||||
IClock* clock, ITimer* timer, LogLevel verbosity);
|
||||
|
||||
// This is a variant of the above function that allows the caller to pass a
|
||||
// Sandbox ID. Platforms that use Sandbox IDs should use this initalize()
|
||||
// function instead of the previous one. Platforms that do not use Sandbox IDs
|
||||
// should not use this version of initialize().
|
||||
static Status initialize(SecureOutputType secure_output_type,
|
||||
const ClientInfo& client_info, IStorage* storage,
|
||||
IClock* clock, ITimer* timer, LogLevel verbosity,
|
||||
const std::string& sandbox_id);
|
||||
|
||||
// Query the CDM library version.
|
||||
static const char* version();
|
||||
|
||||
@@ -365,6 +376,21 @@ class CDM_EXPORT Cdm : public ITimerClient {
|
||||
static Cdm* create(IEventListener* listener, IStorage* storage,
|
||||
bool privacy_mode);
|
||||
|
||||
// This is a variant of the above function that allows the caller to specify
|
||||
// that the IStorage should be treated as read-only. Passing true for this
|
||||
// parameter will cause the Widevine CE CDM to prevent attempts to modify any
|
||||
// data in the IStorage. Note that this is *not* the expected operation mode
|
||||
// for most clients and will likely lead to playback failures. It should only
|
||||
// be used in cases where read-only certificates and licenses have been
|
||||
// pre-loaded on a device, such as the preloaded licenses in ATSC 3.
|
||||
//
|
||||
// It is not possible to mix read-only and non-read-only files in the same
|
||||
// IStorage instance. A separate CDM with a separate IStorage pointing to the
|
||||
// non-read-only files should be created with the read-only flag omitted or
|
||||
// set to false.
|
||||
static Cdm* create(IEventListener* listener, IStorage* storage,
|
||||
bool privacy_mode, bool storage_is_read_only);
|
||||
|
||||
virtual ~Cdm() {}
|
||||
|
||||
// The following three methods relate to service certificates. A service
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Widevine CE CDM Version
|
||||
#ifndef CDM_VERSION
|
||||
# define CDM_VERSION "15.2.0-non-prod"
|
||||
# define CDM_VERSION "15.3.0"
|
||||
#endif
|
||||
#define EME_VERSION "https://www.w3.org/TR/2017/REC-encrypted-media-20170918"
|
||||
|
||||
@@ -17,6 +17,9 @@ class PropertiesCE {
|
||||
static Cdm::ClientInfo GetClientInfo();
|
||||
static Cdm::SecureOutputType GetSecureOutputType();
|
||||
static void SetProvisioningMessagesAreBinary(bool bin_prov);
|
||||
// If this is set to an empty string, (which is the default) then PropertiesCE
|
||||
// will report that there is no Sandbox ID in use.
|
||||
static void SetSandboxId(const std::string& sandbox_id);
|
||||
|
||||
private:
|
||||
static void SetSecureOutputType(Cdm::SecureOutputType secure_output_type);
|
||||
|
||||
103
cdm/src/cdm.cpp
103
cdm/src/cdm.cpp
@@ -38,6 +38,7 @@ using namespace wvcdm;
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr const char* kNoSandboxId = "";
|
||||
const int64_t kPolicyTimerDurationMilliseconds = 5000;
|
||||
void* const kPolicyTimerContext = nullptr;
|
||||
|
||||
@@ -108,9 +109,59 @@ class PropertySet : public CdmClientPropertySet {
|
||||
const std::string empty_string_;
|
||||
};
|
||||
|
||||
// A wrapper for another IStorage instance that intercepts attempts to write to
|
||||
// that storage and blocks them. Acts as a last-ditch safeguard against the CDM
|
||||
// trying to write erroneously to filesystems where it is critical that we never
|
||||
// overwrite or delete the files. Specifically, preloaded files on ATSC 3.
|
||||
//
|
||||
// TODO(b/148693106): Right now, this wrapper fails writes silently because
|
||||
// there are known bugs where the CDM will still try to write to it. Once these
|
||||
// bugs are resolved, we can change this wrapper to return errors on write
|
||||
// attempts. The wrapper itself will still be necessary, in order to prevent any
|
||||
// future bugs from corrupting ATSC files.
|
||||
class ReadOnlyStorage : public Cdm::IStorage {
|
||||
public:
|
||||
explicit ReadOnlyStorage(Cdm::IStorage* storage) : storage_(storage) {}
|
||||
~ReadOnlyStorage() override {}
|
||||
|
||||
bool read(const std::string& name, std::string* data) override {
|
||||
return storage_->read(name, data);
|
||||
}
|
||||
|
||||
bool write(const std::string& name, const std::string& data) override {
|
||||
// TODO(b/148693106): Once we have resolved the bugs causing the CDM to
|
||||
// erroneously write to read-only files, change this to an error instead of
|
||||
// a silent failure.
|
||||
return true;
|
||||
}
|
||||
|
||||
bool exists(const std::string& name) override {
|
||||
return storage_->exists(name);
|
||||
}
|
||||
|
||||
bool remove(const std::string& name) override {
|
||||
// TODO(b/148693106): Once we have resolved the bugs causing the CDM to
|
||||
// erroneously write to read-only files, change this to an error instead of
|
||||
// a silent failure.
|
||||
return true;
|
||||
}
|
||||
|
||||
int32_t size(const std::string& name) override {
|
||||
return storage_->size(name);
|
||||
}
|
||||
|
||||
bool list(std::vector<std::string>* file_names) override {
|
||||
return storage_->list(file_names);
|
||||
}
|
||||
|
||||
private:
|
||||
Cdm::IStorage* storage_; // Lifetime is managed by the caller
|
||||
};
|
||||
|
||||
class CdmImpl : public Cdm, public WvCdmEventListener {
|
||||
public:
|
||||
CdmImpl(IEventListener* listener, IStorage* storage, bool privacy_mode);
|
||||
CdmImpl(IEventListener* listener, IStorage* storage, bool privacy_mode,
|
||||
const ReadOnlyStorage* owned_storage_object);
|
||||
|
||||
~CdmImpl() override;
|
||||
|
||||
@@ -262,14 +313,23 @@ class CdmImpl : public Cdm, public WvCdmEventListener {
|
||||
};
|
||||
|
||||
std::map<std::string, SessionMetadata> sessions_;
|
||||
|
||||
// This field will be nullptr unless the create() function had to allocate
|
||||
// memory for a read-only storage wrapper that needs to be cleaned up when
|
||||
// this CDM instance is deleted. The storage should always be accessed via
|
||||
// file_system_ and not via this parameter directly. However, it must be
|
||||
// stored so it can be cleaned up later.
|
||||
const std::unique_ptr<const ReadOnlyStorage> owned_storage_object_;
|
||||
};
|
||||
|
||||
CdmImpl::CdmImpl(IEventListener* listener, IStorage* storage, bool privacy_mode)
|
||||
CdmImpl::CdmImpl(IEventListener* listener, IStorage* storage, bool privacy_mode,
|
||||
const ReadOnlyStorage* owned_storage_object)
|
||||
: listener_(listener),
|
||||
policy_timer_enabled_(false),
|
||||
provisioning_service_certificate_(),
|
||||
file_system_("", storage),
|
||||
cdm_engine_(CdmEngineFactory::CreateCdmEngine(&file_system_)) {
|
||||
cdm_engine_(CdmEngineFactory::CreateCdmEngine(&file_system_)),
|
||||
owned_storage_object_(owned_storage_object) {
|
||||
assert(nullptr != listener_);
|
||||
property_set_.set_use_privacy_mode(privacy_mode);
|
||||
}
|
||||
@@ -292,6 +352,12 @@ Cdm::Status CdmImpl::setServiceCertificate(ServiceRole role,
|
||||
|
||||
if (role == kLicensingService || role == kAllServices) {
|
||||
property_set_.set_service_certificate(certificate);
|
||||
|
||||
// Update all open sessions with the new certificate.
|
||||
for (const auto& session_pair : sessions_) {
|
||||
cdm_engine_->SetSessionServiceCertificate(session_pair.first,
|
||||
certificate);
|
||||
}
|
||||
}
|
||||
|
||||
if (role == kProvisioningService || role == kAllServices) {
|
||||
@@ -680,6 +746,10 @@ Cdm::Status CdmImpl::generateRequest(const std::string& session_id,
|
||||
} else if (result == NEED_PROVISIONING) {
|
||||
LOGE("Device not provisioned");
|
||||
return kNeedsDeviceCertificate;
|
||||
} else if (result == PRIVACY_MODE_ERROR_1 || result == PRIVACY_MODE_ERROR_2 ||
|
||||
result == PRIVACY_MODE_ERROR_3) {
|
||||
LOGE("No licensing service certificate installed");
|
||||
return kNeedsServiceCertificate;
|
||||
} else if (result != KEY_MESSAGE) {
|
||||
LOGE("Unexpected error %d", result);
|
||||
return kUnexpectedError;
|
||||
@@ -1501,6 +1571,8 @@ Cdm::Status CdmImpl::ConvertHdcpLevel(const std::string& query_value,
|
||||
*result = kHdcp2_1;
|
||||
} else if (query_value == QUERY_VALUE_HDCP_V2_2) {
|
||||
*result = kHdcp2_2;
|
||||
} else if (query_value == QUERY_VALUE_HDCP_V2_3) {
|
||||
*result = kHdcp2_3;
|
||||
} else {
|
||||
return kUnexpectedError;
|
||||
}
|
||||
@@ -1513,6 +1585,15 @@ Cdm::Status CdmImpl::ConvertHdcpLevel(const std::string& query_value,
|
||||
Cdm::Status Cdm::initialize(SecureOutputType secure_output_type,
|
||||
const ClientInfo& client_info, IStorage* storage,
|
||||
IClock* clock, ITimer* timer, LogLevel verbosity) {
|
||||
return initialize(secure_output_type, client_info, storage, clock, timer,
|
||||
verbosity, kNoSandboxId);
|
||||
}
|
||||
|
||||
// static
|
||||
Cdm::Status Cdm::initialize(SecureOutputType secure_output_type,
|
||||
const ClientInfo& client_info, IStorage* storage,
|
||||
IClock* clock, ITimer* timer, LogLevel verbosity,
|
||||
const std::string& sandbox_id) {
|
||||
// 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);
|
||||
@@ -1540,6 +1621,7 @@ Cdm::Status Cdm::initialize(SecureOutputType secure_output_type,
|
||||
|
||||
PropertiesCE::SetSecureOutputType(secure_output_type);
|
||||
PropertiesCE::SetClientInfo(client_info);
|
||||
if (sandbox_id != kNoSandboxId) PropertiesCE::SetSandboxId(sandbox_id);
|
||||
Properties::Init();
|
||||
host.storage = storage;
|
||||
host.clock = clock;
|
||||
@@ -1554,6 +1636,13 @@ const char* Cdm::version() { return CDM_VERSION; }
|
||||
// static
|
||||
Cdm* Cdm::create(IEventListener* listener, IStorage* storage,
|
||||
bool privacy_mode) {
|
||||
return create(listener, storage, privacy_mode,
|
||||
false /* storage_is_read_only */);
|
||||
}
|
||||
|
||||
// static
|
||||
Cdm* Cdm::create(IEventListener* listener, IStorage* storage, bool privacy_mode,
|
||||
bool storage_is_read_only) {
|
||||
if (!host.initialized) {
|
||||
LOGE("Not initialized!");
|
||||
return nullptr;
|
||||
@@ -1567,7 +1656,13 @@ Cdm* Cdm::create(IEventListener* listener, IStorage* storage,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new CdmImpl(listener, storage, privacy_mode);
|
||||
if (storage_is_read_only) {
|
||||
ReadOnlyStorage* read_only_storage = new ReadOnlyStorage(storage);
|
||||
return new CdmImpl(listener, read_only_storage, privacy_mode,
|
||||
read_only_storage);
|
||||
} else {
|
||||
return new CdmImpl(listener, storage, privacy_mode, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
@@ -15,6 +15,7 @@ bool use_secure_buffers_ = false;
|
||||
bool use_fifo_ = false;
|
||||
bool use_userspace_buffers_ = true;
|
||||
bool set_provisioning_messages_to_binary_ = false;
|
||||
std::string sandbox_id_;
|
||||
|
||||
widevine::Cdm::SecureOutputType secure_output_type_ =
|
||||
widevine::Cdm::kNoSecureOutput;
|
||||
@@ -75,6 +76,11 @@ void PropertiesCE::SetProvisioningMessagesAreBinary(bool new_setting) {
|
||||
set_provisioning_messages_to_binary_ = new_setting;
|
||||
}
|
||||
|
||||
// static
|
||||
void PropertiesCE::SetSandboxId(const std::string& sandbox_id) {
|
||||
sandbox_id_ = sandbox_id;
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
namespace wvcdm {
|
||||
@@ -128,6 +134,7 @@ bool Properties::GetWVCdmVersion(std::string* version) {
|
||||
// static
|
||||
bool Properties::GetDeviceFilesBasePath(CdmSecurityLevel,
|
||||
std::string* base_path) {
|
||||
if (base_path == nullptr) return false;
|
||||
// A no-op, but successful.
|
||||
base_path->clear();
|
||||
return true;
|
||||
@@ -146,7 +153,11 @@ bool Properties::GetOEMCryptoPath(std::string*) {
|
||||
}
|
||||
|
||||
// static
|
||||
bool Properties::GetSandboxId(std::string* /* sandbox_id */) { return false; }
|
||||
bool Properties::GetSandboxId(std::string* sandbox_id_ptr) {
|
||||
if (sandbox_id_.empty() || sandbox_id_ptr == nullptr) return false;
|
||||
(*sandbox_id_ptr) = sandbox_id_;
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool Properties::AlwaysUseKeySetIds() { return true; }
|
||||
|
||||
@@ -216,7 +216,7 @@ class CdmTest : public WvCdmTestBase, public Cdm::IEventListener {
|
||||
// Reinit the library.
|
||||
Cdm::Status status = Cdm::initialize(
|
||||
Cdm::kNoSecureOutput, PropertiesCE::GetClientInfo(), g_host, g_host,
|
||||
g_host, static_cast<Cdm::LogLevel>(g_cutoff));
|
||||
g_host, static_cast<Cdm::LogLevel>(g_cutoff), g_sandbox_id);
|
||||
ASSERT_EQ(Cdm::kSuccess, status);
|
||||
|
||||
// Make a fresh CDM.
|
||||
@@ -541,12 +541,11 @@ TEST_F(CdmTest, TestHostTimer) {
|
||||
}
|
||||
|
||||
TEST_F(CdmTest, Initialize) {
|
||||
Cdm::Status status;
|
||||
|
||||
// Try with an invalid output type.
|
||||
status = Cdm::initialize(static_cast<Cdm::SecureOutputType>(-1),
|
||||
PropertiesCE::GetClientInfo(), g_host, g_host,
|
||||
g_host, static_cast<Cdm::LogLevel>(g_cutoff));
|
||||
Cdm::Status status =
|
||||
Cdm::initialize(static_cast<Cdm::SecureOutputType>(-1),
|
||||
PropertiesCE::GetClientInfo(), g_host, g_host, g_host,
|
||||
static_cast<Cdm::LogLevel>(g_cutoff), g_sandbox_id);
|
||||
EXPECT_EQ(Cdm::kTypeError, status);
|
||||
|
||||
// Try with various client info properties missing.
|
||||
@@ -555,77 +554,77 @@ TEST_F(CdmTest, Initialize) {
|
||||
|
||||
broken_client_info = working_client_info;
|
||||
broken_client_info.product_name.clear();
|
||||
status =
|
||||
Cdm::initialize(Cdm::kNoSecureOutput, broken_client_info, g_host, g_host,
|
||||
g_host, static_cast<Cdm::LogLevel>(g_cutoff));
|
||||
status = Cdm::initialize(Cdm::kNoSecureOutput, broken_client_info, g_host,
|
||||
g_host, g_host, static_cast<Cdm::LogLevel>(g_cutoff),
|
||||
g_sandbox_id);
|
||||
EXPECT_EQ(Cdm::kTypeError, status);
|
||||
|
||||
broken_client_info = working_client_info;
|
||||
broken_client_info.company_name.clear();
|
||||
status =
|
||||
Cdm::initialize(Cdm::kNoSecureOutput, broken_client_info, g_host, g_host,
|
||||
g_host, static_cast<Cdm::LogLevel>(g_cutoff));
|
||||
status = Cdm::initialize(Cdm::kNoSecureOutput, broken_client_info, g_host,
|
||||
g_host, g_host, static_cast<Cdm::LogLevel>(g_cutoff),
|
||||
g_sandbox_id);
|
||||
EXPECT_EQ(Cdm::kTypeError, status);
|
||||
|
||||
broken_client_info = working_client_info;
|
||||
broken_client_info.device_name.clear(); // Not required
|
||||
status =
|
||||
Cdm::initialize(Cdm::kNoSecureOutput, broken_client_info, g_host, g_host,
|
||||
g_host, static_cast<Cdm::LogLevel>(g_cutoff));
|
||||
status = Cdm::initialize(Cdm::kNoSecureOutput, broken_client_info, g_host,
|
||||
g_host, g_host, static_cast<Cdm::LogLevel>(g_cutoff),
|
||||
g_sandbox_id);
|
||||
EXPECT_EQ(Cdm::kSuccess, status);
|
||||
|
||||
broken_client_info = working_client_info;
|
||||
broken_client_info.model_name.clear();
|
||||
status =
|
||||
Cdm::initialize(Cdm::kNoSecureOutput, broken_client_info, g_host, g_host,
|
||||
g_host, static_cast<Cdm::LogLevel>(g_cutoff));
|
||||
status = Cdm::initialize(Cdm::kNoSecureOutput, broken_client_info, g_host,
|
||||
g_host, g_host, static_cast<Cdm::LogLevel>(g_cutoff),
|
||||
g_sandbox_id);
|
||||
EXPECT_EQ(Cdm::kTypeError, status);
|
||||
|
||||
broken_client_info = working_client_info;
|
||||
broken_client_info.arch_name.clear(); // Not required
|
||||
status =
|
||||
Cdm::initialize(Cdm::kNoSecureOutput, broken_client_info, g_host, g_host,
|
||||
g_host, static_cast<Cdm::LogLevel>(g_cutoff));
|
||||
status = Cdm::initialize(Cdm::kNoSecureOutput, broken_client_info, g_host,
|
||||
g_host, g_host, static_cast<Cdm::LogLevel>(g_cutoff),
|
||||
g_sandbox_id);
|
||||
EXPECT_EQ(Cdm::kSuccess, status);
|
||||
|
||||
broken_client_info = working_client_info;
|
||||
broken_client_info.build_info.clear(); // Not required
|
||||
status =
|
||||
Cdm::initialize(Cdm::kNoSecureOutput, broken_client_info, g_host, g_host,
|
||||
g_host, static_cast<Cdm::LogLevel>(g_cutoff));
|
||||
status = Cdm::initialize(Cdm::kNoSecureOutput, broken_client_info, g_host,
|
||||
g_host, g_host, static_cast<Cdm::LogLevel>(g_cutoff),
|
||||
g_sandbox_id);
|
||||
EXPECT_EQ(Cdm::kSuccess, status);
|
||||
|
||||
// Try with various host interfaces missing.
|
||||
status =
|
||||
Cdm::initialize(Cdm::kNoSecureOutput, working_client_info, nullptr,
|
||||
g_host, g_host, static_cast<Cdm::LogLevel>(g_cutoff));
|
||||
status = Cdm::initialize(Cdm::kNoSecureOutput, working_client_info, nullptr,
|
||||
g_host, g_host, static_cast<Cdm::LogLevel>(g_cutoff),
|
||||
g_sandbox_id);
|
||||
EXPECT_EQ(Cdm::kTypeError, status);
|
||||
|
||||
status =
|
||||
Cdm::initialize(Cdm::kNoSecureOutput, working_client_info, g_host,
|
||||
nullptr, g_host, static_cast<Cdm::LogLevel>(g_cutoff));
|
||||
status = Cdm::initialize(Cdm::kNoSecureOutput, working_client_info, g_host,
|
||||
nullptr, g_host,
|
||||
static_cast<Cdm::LogLevel>(g_cutoff), g_sandbox_id);
|
||||
EXPECT_EQ(Cdm::kTypeError, status);
|
||||
|
||||
status =
|
||||
Cdm::initialize(Cdm::kNoSecureOutput, working_client_info, g_host, g_host,
|
||||
nullptr, static_cast<Cdm::LogLevel>(g_cutoff));
|
||||
status = Cdm::initialize(Cdm::kNoSecureOutput, working_client_info, g_host,
|
||||
g_host, nullptr,
|
||||
static_cast<Cdm::LogLevel>(g_cutoff), g_sandbox_id);
|
||||
EXPECT_EQ(Cdm::kTypeError, status);
|
||||
|
||||
// Try all output types.
|
||||
status =
|
||||
Cdm::initialize(Cdm::kDirectRender, working_client_info, g_host, g_host,
|
||||
g_host, static_cast<Cdm::LogLevel>(g_cutoff));
|
||||
status = Cdm::initialize(Cdm::kDirectRender, working_client_info, g_host,
|
||||
g_host, g_host, static_cast<Cdm::LogLevel>(g_cutoff),
|
||||
g_sandbox_id);
|
||||
EXPECT_EQ(Cdm::kSuccess, status);
|
||||
|
||||
status =
|
||||
Cdm::initialize(Cdm::kOpaqueHandle, working_client_info, g_host, g_host,
|
||||
g_host, static_cast<Cdm::LogLevel>(g_cutoff));
|
||||
status = Cdm::initialize(Cdm::kOpaqueHandle, working_client_info, g_host,
|
||||
g_host, g_host, static_cast<Cdm::LogLevel>(g_cutoff),
|
||||
g_sandbox_id);
|
||||
EXPECT_EQ(Cdm::kSuccess, status);
|
||||
|
||||
// One last init with everything correct and working.
|
||||
status =
|
||||
Cdm::initialize(Cdm::kNoSecureOutput, working_client_info, g_host, g_host,
|
||||
g_host, static_cast<Cdm::LogLevel>(g_cutoff));
|
||||
status = Cdm::initialize(Cdm::kNoSecureOutput, working_client_info, g_host,
|
||||
g_host, g_host, static_cast<Cdm::LogLevel>(g_cutoff),
|
||||
g_sandbox_id);
|
||||
EXPECT_EQ(Cdm::kSuccess, status);
|
||||
}
|
||||
|
||||
@@ -747,6 +746,34 @@ TEST_F(CdmTest, SetServiceCertificate) {
|
||||
EXPECT_EQ(Cdm::kTypeError, status);
|
||||
}
|
||||
|
||||
TEST_F(CdmTest, OpenSessionWithoutServiceCertificate) {
|
||||
// Create a CDM instance that does not have any service certificates
|
||||
// installed.
|
||||
ASSERT_NO_FATAL_FAILURE(CreateAdditionalCdm(true /* privacy_mode */, &cdm_));
|
||||
EnsureProvisioned();
|
||||
|
||||
// Verify that sessions can be opened.
|
||||
std::string session_id;
|
||||
ASSERT_EQ(Cdm::kSuccess, cdm_->createSession(Cdm::kTemporary, &session_id));
|
||||
|
||||
// License request generation, however, should fail.
|
||||
EXPECT_CALL(*this, onMessage(session_id, _, _)).Times(0);
|
||||
EXPECT_EQ(Cdm::kNeedsServiceCertificate,
|
||||
generateRequestWithRetry(session_id, Cdm::kCenc, kCencInitData));
|
||||
Mock::VerifyAndClear(this);
|
||||
|
||||
// Once a service certificate has been set, the existing session should be
|
||||
// able to generate a request.
|
||||
ASSERT_EQ(Cdm::kSuccess,
|
||||
cdm_->setServiceCertificate(Cdm::kLicensingService,
|
||||
config_.license_service_certificate()));
|
||||
|
||||
EXPECT_CALL(*this, onMessage(session_id, _, _)).Times(AtLeast(1));
|
||||
EXPECT_EQ(Cdm::kSuccess,
|
||||
generateRequestWithRetry(session_id, Cdm::kCenc, kCencInitData));
|
||||
Mock::VerifyAndClear(this);
|
||||
}
|
||||
|
||||
TEST_F(CdmTest, GetRobustnessLevel) {
|
||||
Cdm::RobustnessLevel level;
|
||||
Cdm::Status status = cdm_->getRobustnessLevel(&level);
|
||||
@@ -1981,6 +2008,10 @@ TEST_F(CdmTest, GetStatusForHdcpResolution) {
|
||||
ASSERT_EQ(Cdm::kSuccess,
|
||||
cdm_->getStatusForHdcpVersion(Cdm::kHdcp2_2, &key_status));
|
||||
EXPECT_THAT(key_status, AnyOf(Cdm::kUsable, Cdm::kOutputRestricted));
|
||||
|
||||
ASSERT_EQ(Cdm::kSuccess,
|
||||
cdm_->getStatusForHdcpVersion(Cdm::kHdcp2_3, &key_status));
|
||||
EXPECT_THAT(key_status, AnyOf(Cdm::kUsable, Cdm::kOutputRestricted));
|
||||
}
|
||||
|
||||
TEST_F(CdmTest, HandlesKeyRotationWithOnlyOneLicenseRequest) {
|
||||
|
||||
@@ -4,7 +4,11 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <sys/utsname.h>
|
||||
@@ -19,11 +23,34 @@
|
||||
using namespace widevine;
|
||||
|
||||
TestHost* g_host = nullptr;
|
||||
std::string g_sandbox_id = "";
|
||||
|
||||
namespace {
|
||||
constexpr const char kSandboxIdParam[] = "--sandbox_id=";
|
||||
|
||||
// Following the pattern established by help text in test_base.cpp
|
||||
constexpr const char kExtraHelpText[] =
|
||||
" --sandbox_id=<sandbox_id>\n"
|
||||
" Specifies the Sandbox ID that should be sent to OEMCrypto via\n"
|
||||
" OEMCrypto_SetSandbox(). On most platforms, since Sandbox IDs are not\n"
|
||||
" in use, this parameter should be omitted.\n";
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
// Init gtest and let it consume arguments.
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
|
||||
// Find and filter out the Sandbox ID, if any.
|
||||
std::vector<std::string> args(argv, argv + argc);
|
||||
auto sandbox_id_iter = std::find_if(std::begin(args) + 1, std::end(args),
|
||||
[](const std::string& elem) -> bool {
|
||||
return elem.find(kSandboxIdParam) == 0;
|
||||
});
|
||||
if (sandbox_id_iter != std::end(args)) {
|
||||
g_sandbox_id = sandbox_id_iter->substr(strlen(kSandboxIdParam));
|
||||
args.erase(sandbox_id_iter);
|
||||
}
|
||||
|
||||
// Set up a Host and initialize the library. This makes these services
|
||||
// available to the tests. We would do this in the test suite itself, but the
|
||||
// core & OEMCrypto tests don't know they depend on this for storage.
|
||||
@@ -46,15 +73,22 @@ int main(int argc, char** argv) {
|
||||
#endif
|
||||
client_info.build_info = __DATE__;
|
||||
|
||||
Cdm::Status status =
|
||||
Cdm::initialize(Cdm::kNoSecureOutput, client_info, g_host, g_host, g_host,
|
||||
static_cast<Cdm::LogLevel>(wvcdm::g_cutoff));
|
||||
Cdm::Status status = Cdm::initialize(
|
||||
Cdm::kNoSecureOutput, client_info, g_host, g_host, g_host,
|
||||
static_cast<Cdm::LogLevel>(wvcdm::g_cutoff), g_sandbox_id);
|
||||
(void)status; // status is now used when assertions are turned off.
|
||||
assert(status == Cdm::kSuccess);
|
||||
|
||||
// This must take place after the call to Cdm::initialize() because it may
|
||||
// make calls that are only valid after the library is initialized.
|
||||
if (!wvcdm::WvCdmTestBase::Initialize(argc, argv)) return 0;
|
||||
std::vector<const char*> new_argv(args.size());
|
||||
std::transform(
|
||||
std::begin(args), std::end(args), std::begin(new_argv),
|
||||
[](const std::string& arg) -> const char* { return arg.c_str(); });
|
||||
// This must take place after the call to Cdm::initialize() because it makes
|
||||
// calls that are only valid after the library is initialized.
|
||||
if (!wvcdm::WvCdmTestBase::Initialize(new_argv.size(), new_argv.data(),
|
||||
kExtraHelpText)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
@@ -31,18 +31,7 @@ void PrintTo(const Cdm::Status& value, ::std::ostream* os) {
|
||||
case Cdm::kSuccess:
|
||||
*os << "Cdm::kSuccess";
|
||||
break;
|
||||
case Cdm::kNeedsDeviceCertificate:
|
||||
*os << "Cdm::kNeedsDeviceCertificate";
|
||||
break;
|
||||
case Cdm::kSessionNotFound:
|
||||
*os << "Cdm::kSessionNotFound";
|
||||
break;
|
||||
case Cdm::kDecryptError:
|
||||
*os << "Cdm::kDecryptError";
|
||||
break;
|
||||
case Cdm::kNoKey:
|
||||
*os << "Cdm::kNoKey";
|
||||
break;
|
||||
|
||||
case Cdm::kTypeError:
|
||||
*os << "Cdm::kTypeError";
|
||||
break;
|
||||
@@ -55,12 +44,45 @@ void PrintTo(const Cdm::Status& value, ::std::ostream* os) {
|
||||
case Cdm::kQuotaExceeded:
|
||||
*os << "Cdm::kQuotaExceeded";
|
||||
break;
|
||||
|
||||
case Cdm::kNeedsDeviceCertificate:
|
||||
*os << "Cdm::kNeedsDeviceCertificate";
|
||||
break;
|
||||
case Cdm::kSessionNotFound:
|
||||
*os << "Cdm::kSessionNotFound";
|
||||
break;
|
||||
case Cdm::kDecryptError:
|
||||
*os << "Cdm::kDecryptError";
|
||||
break;
|
||||
case Cdm::kNoKey:
|
||||
*os << "Cdm::kNoKey";
|
||||
break;
|
||||
case Cdm::kKeyUsageBlockedByPolicy:
|
||||
*os << "Cdm::kKeyUsageBlockedByPolicy";
|
||||
break;
|
||||
case Cdm::kRangeError:
|
||||
*os << "Cdm::kRangeError";
|
||||
break;
|
||||
case Cdm::kResourceContention:
|
||||
*os << "Cdm::kResourceContention";
|
||||
break;
|
||||
case Cdm::kSessionStateLost:
|
||||
*os << "Cdm::kSessionStateLost";
|
||||
break;
|
||||
case Cdm::kSystemStateLost:
|
||||
*os << "Cdm::kSystemStateLost";
|
||||
break;
|
||||
case Cdm::kOutputTooLarge:
|
||||
*os << "Cdm::kOutputTooLarge";
|
||||
break;
|
||||
case Cdm::kNeedsServiceCertificate:
|
||||
*os << "Cdm::kNeedsServiceCertificate";
|
||||
break;
|
||||
|
||||
case Cdm::kUnexpectedError:
|
||||
*os << "Cdm::kUnexpectedError";
|
||||
break;
|
||||
|
||||
default:
|
||||
*os << "Unknown Cdm::Status value " << value;
|
||||
break;
|
||||
@@ -84,6 +106,9 @@ void PrintTo(const Cdm::KeyStatus& value, ::std::ostream* os) {
|
||||
case Cdm::kInternalError:
|
||||
*os << "Cdm::kInternalError";
|
||||
break;
|
||||
case Cdm::kReleased:
|
||||
*os << "Cdm::kReleased";
|
||||
break;
|
||||
default:
|
||||
*os << "Unknown Cdm::KeyStatus value " << value;
|
||||
break;
|
||||
|
||||
@@ -66,5 +66,6 @@ class TestHost : public widevine::Cdm::IStorage,
|
||||
|
||||
// Owned and managed by the test runner.
|
||||
extern TestHost* g_host;
|
||||
extern std::string g_sandbox_id;
|
||||
|
||||
#endif // WVCDM_CDM_TEST_TEST_HOST_H_
|
||||
|
||||
Reference in New Issue
Block a user