Source release 15.1.0

This commit is contained in:
John W. Bruce
2019-03-29 18:16:05 -07:00
parent 66628486b5
commit 2b26dee09c
44 changed files with 1371 additions and 356 deletions

View File

@@ -76,13 +76,20 @@ class CDM_EXPORT Cdm : public ITimerClient {
// that support the EME API, these codes should be handled in the system
// layer. If it is necessary to notify the client application of one of
// these statuses, it should be mapped to one of the exception codes defined
// in the EME specification.
// in the EME specification. Some of these errors are considered
// "recoverable" in that there are specific known remedies that the client
// may take in response to them. See the Integration Guide for further
// information.
kNeedsDeviceCertificate = 101,
kSessionNotFound = 102,
kDecryptError = 103,
kNoKey = 104,
kKeyUsageBlockedByPolicy = 105,
kRangeError = 106,
kResourceContention = 107, // Recoverable
kSessionStateLost = 108, // Recoverable
kSystemStateLost = 109, // Recoverable
kOutputTooLarge = 109, // Recoverable
// This covers errors that we do not expect (see logs for details):
kUnexpectedError = 99999,
@@ -178,6 +185,13 @@ class CDM_EXPORT Cdm : public ITimerClient {
kLicensingService = 2,
} ServiceRole;
// These are the available Widevine robustness levels.
typedef enum {
kL1 = 1,
kL2 = 2,
kL3 = 3,
} RobustnessLevel;
// A map of key statuses.
// See Cdm::getKeyStatuses().
typedef std::map<std::string, KeyStatus> KeyStatusMap;
@@ -396,6 +410,21 @@ class CDM_EXPORT Cdm : public ITimerClient {
ServiceRole role, const std::string& response,
std::string* certificate) = 0;
// Returns the robustness level of the device, as reported by OEMCrypto. Note
// that this function is *not* cryptographically secure and it should only be
// relied upon for informational purposes (e.g. determining which content to
// show in the UI) and not security purposes. (e.g. determining which content
// to allow the device to play) *Only* secure communication between OEMCrypto
// and the license service should be used to make security decisions.
virtual Status getRobustnessLevel(RobustnessLevel* level) = 0;
// Returns the resource rating tier of the device, as reported by OEMCrypto.
virtual Status getResourceRatingTier(uint32_t* tier) = 0;
// Retrieves the build information for the underlying OEMCrypto
// implementation.
virtual Status getOemCryptoBuildInfo(std::string* build_info) = 0;
// Determine if the device has a Device Certificate (for the current origin).
// The Device Certificate is origin-specific, and the origin is
// dertermined by the CDM's current IStorage object.

View File

@@ -1,3 +1,3 @@
// Widevine CE CDM Version
#define CDM_VERSION "15.0.0"
#define CDM_VERSION "15.1.0"
#define EME_VERSION "https://www.w3.org/TR/2017/REC-encrypted-media-20170918"

View File

@@ -26,6 +26,7 @@
#include "string_conversions.h"
#include "wv_cdm_constants.h"
#include "wv_cdm_event_listener.h"
#include "wv_cdm_types.h"
// CE:
#include "cdm_version.h"
@@ -123,6 +124,12 @@ class CdmImpl : public Cdm, public WvCdmEventListener {
ServiceRole role, const std::string& response,
std::string* certificate) override;
Status getRobustnessLevel(RobustnessLevel* level) override;
Status getResourceRatingTier(uint32_t* tier) override;
Status getOemCryptoBuildInfo(std::string* build_info) override;
bool isProvisioned() override;
Status getProvisioningRequest(std::string* request) override;
@@ -316,11 +323,87 @@ Cdm::Status CdmImpl::parseAndLoadServiceCertificateResponse(
LOGE("Failure parsing service certificate response!");
return kTypeError;
}
if (certificate)
*certificate = parsed_cert;
if (certificate) *certificate = parsed_cert;
return setServiceCertificate(role, parsed_cert);
}
Cdm::Status CdmImpl::getRobustnessLevel(RobustnessLevel* level) {
if (level == nullptr) {
LOGE("Missing level parameter to receive robustness level.");
return kTypeError;
}
std::string level_string;
CdmResponseType result = cdm_engine_->QueryStatus(
kLevelDefault, QUERY_KEY_SECURITY_LEVEL, &level_string);
if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
} else if (result != NO_ERROR) {
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
if (level_string == QUERY_VALUE_SECURITY_LEVEL_L1) {
*level = kL1;
} else if (level_string == QUERY_VALUE_SECURITY_LEVEL_L2) {
*level = kL2;
} else if (level_string == QUERY_VALUE_SECURITY_LEVEL_L3) {
*level = kL3;
} else {
LOGE("Unknown robustness level: %s", level_string.c_str());
return kUnexpectedError;
}
return kSuccess;
}
Cdm::Status CdmImpl::getResourceRatingTier(uint32_t* tier) {
if (tier == nullptr) {
LOGE("Missing tier parameter to receive resource rating tier.");
return kTypeError;
}
std::string tier_string;
CdmResponseType result = cdm_engine_->QueryStatus(
kLevelDefault, QUERY_KEY_RESOURCE_RATING_TIER, &tier_string);
if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
} else if (result != NO_ERROR) {
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
uint32_t parsed_tier = static_cast<uint32_t>(std::stoul(tier_string));
if (parsed_tier <= 0) {
LOGE("Invalid resource rating tier %lu", parsed_tier);
return kUnexpectedError;
}
*tier = parsed_tier;
return kSuccess;
}
Cdm::Status CdmImpl::getOemCryptoBuildInfo(std::string* build_info) {
if (build_info == nullptr) {
LOGE("Missing build_info parameter to receive build info.");
return kTypeError;
}
CdmResponseType result = cdm_engine_->QueryStatus(
kLevelDefault, QUERY_KEY_OEMCRYPTO_BUILD_INFORMATION, build_info);
if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
} else if (result != NO_ERROR) {
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
return kSuccess;
}
bool CdmImpl::isProvisioned() {
return cdm_engine_->IsProvisioned(kSecurityLevelL1);
}
@@ -333,9 +416,11 @@ Cdm::Status CdmImpl::getProvisioningRequest(std::string* request) {
request, &ignored_base_url);
if (result == CERT_PROVISIONING_NONCE_GENERATION_ERROR) {
LOGE("Nonce quota exceeded");
return kQuotaExceeded;
}
if (result != NO_ERROR) {
return kResourceContention;
} else if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
} else if (result != NO_ERROR) {
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
@@ -348,7 +433,10 @@ Cdm::Status CdmImpl::handleProvisioningResponse(const std::string& response) {
CdmResponseType result = cdm_engine_->HandleProvisioningResponse(
response, &ignored_cert, &ignored_wrapped_key);
if (result != NO_ERROR) {
if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
} else if (result != NO_ERROR) {
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
@@ -357,14 +445,24 @@ Cdm::Status CdmImpl::handleProvisioningResponse(const std::string& response) {
}
Cdm::Status CdmImpl::removeProvisioning() {
if (cdm_engine_->Unprovision(kSecurityLevelL1) != NO_ERROR) {
CdmResponseType result = cdm_engine_->Unprovision(kSecurityLevelL1);
if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
} else if (result != NO_ERROR) {
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
return kSuccess;
}
Cdm::Status CdmImpl::removeUsageTable() {
if (cdm_engine_->DeleteUsageTable(kSecurityLevelL1) != NO_ERROR) {
CdmResponseType result = cdm_engine_->DeleteUsageTable(kSecurityLevelL1);
if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
} else if (result != NO_ERROR) {
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
return kSuccess;
@@ -375,8 +473,13 @@ Cdm::Status CdmImpl::listStoredLicenses(std::vector<std::string>* key_set_ids) {
LOGE("Missing vector parameter to receive key_set_ids.");
return kTypeError;
}
if (cdm_engine_->ListStoredLicenses(kSecurityLevelL1, key_set_ids) !=
NO_ERROR) {
CdmResponseType result =
cdm_engine_->ListStoredLicenses(kSecurityLevelL1, key_set_ids);
if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
} else if (result != NO_ERROR) {
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
return kSuccess;
@@ -387,24 +490,39 @@ Cdm::Status CdmImpl::listUsageRecords(std::vector<std::string>* ksids) {
LOGE("Missing vector parameter to receive KSIDs.");
return kTypeError;
}
if (cdm_engine_->ListUsageIds(property_set_.app_id(), kSecurityLevelL1, ksids,
nullptr) != NO_ERROR) {
CdmResponseType result = cdm_engine_->ListUsageIds(
property_set_.app_id(), kSecurityLevelL1, ksids, nullptr);
if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
} else if (result != NO_ERROR) {
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
return kSuccess;
}
Cdm::Status CdmImpl::deleteUsageRecord(const std::string& key_set_id) {
if (cdm_engine_->DeleteUsageRecord(property_set_.app_id(), kSecurityLevelL1,
key_set_id) != NO_ERROR) {
CdmResponseType result = cdm_engine_->DeleteUsageRecord(
property_set_.app_id(), kSecurityLevelL1, key_set_id);
if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
} else if (result != NO_ERROR) {
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
return kSuccess;
}
Cdm::Status CdmImpl::deleteAllUsageRecords() {
if (cdm_engine_->RemoveAllUsageInfo(property_set_.app_id(),
kSecurityLevelL1) != NO_ERROR) {
CdmResponseType result =
cdm_engine_->RemoveAllUsageInfo(property_set_.app_id(), kSecurityLevelL1);
if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
} else if (result != NO_ERROR) {
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
return kSuccess;
@@ -466,6 +584,9 @@ Cdm::Status CdmImpl::createSession(SessionType session_type,
// misleading ID to the application.
session_id->clear();
return kNeedsDeviceCertificate;
case SYSTEM_INVALIDATED_ERROR:
LOGE("System invalidated");
return kSystemStateLost;
default:
LOGE("Unexpected error %d", result);
return kUnexpectedError;
@@ -547,17 +668,19 @@ 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) {
if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
} else if (result == SESSION_LOST_STATE_ERROR) {
LOGE("Session invalidated");
return kSessionStateLost;
} else if (result == LICENSE_REQUEST_NONCE_GENERATION_ERROR) {
LOGE("Nonce quota exceeded");
return kQuotaExceeded;
}
if (result == NEED_PROVISIONING) {
return kResourceContention;
} else if (result == NEED_PROVISIONING) {
LOGE("Device not provisioned");
return kNeedsDeviceCertificate;
}
if (result != KEY_MESSAGE) {
} else if (result != KEY_MESSAGE) {
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
@@ -586,6 +709,9 @@ Cdm::Status CdmImpl::load(const std::string& session_id) {
switch (result) {
case NO_ERROR:
break;
case SYSTEM_INVALIDATED_ERROR:
LOGE("System invalidated");
return kSystemStateLost;
case NEED_PROVISIONING:
return kNeedsDeviceCertificate;
default:
@@ -602,15 +728,20 @@ Cdm::Status CdmImpl::load(const std::string& session_id) {
if (!f.LicenseExists(session_id)) {
// This might be a usage record session which needs to be loaded.
CdmKeyMessage ignored_release_message;
result = cdm_engine_->LoadUsageSession(session_id, &ignored_release_message);
if (result == LOAD_USAGE_INFO_MISSING) {
LOGE("Unable to load license: %s", session_id.c_str());
result =
cdm_engine_->LoadUsageSession(session_id, &ignored_release_message);
if (result != KEY_MESSAGE) {
cdm_engine_->CloseSession(session_id);
return kSessionNotFound;
} else if (result != KEY_MESSAGE) {
LOGE("Unexpected error %d", result);
cdm_engine_->CloseSession(session_id);
return kUnexpectedError;
if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
} else if (result == LOAD_USAGE_INFO_MISSING) {
LOGE("Unable to load license: %s", session_id.c_str());
return kSessionNotFound;
} else {
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
}
sessions_[session_id].type = kPersistentUsageRecord;
@@ -622,6 +753,9 @@ Cdm::Status CdmImpl::load(const std::string& session_id) {
if (result == GET_RELEASED_LICENSE_ERROR) {
// This was partially removed already.
// The EME spec states that we should be able to load it, but not use it.
} else if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
} else if (result != KEY_ADDED) {
LOGE("Unexpected error %d", result);
return kUnexpectedError;
@@ -665,7 +799,13 @@ Cdm::Status CdmImpl::update(const std::string& session_id,
// 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) {
if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
} else if (result == SESSION_LOST_STATE_ERROR) {
LOGE("Session invalidated");
return kSessionStateLost;
} else if (result == OFFLINE_LICENSE_PROHIBITED) {
LOGE("A temporary session cannot be used for a persistent license.");
return kRangeError;
} else if (result == STORAGE_PROHIBITED) {
@@ -724,11 +864,16 @@ Cdm::Status CdmImpl::loadEmbeddedKeys(const std::string& session_id,
session_id, session_id, init_data_obj, kLicenseTypeEmbeddedKeyData,
app_parameters_, &key_request);
if (result == LICENSE_REQUEST_NONCE_GENERATION_ERROR) {
if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
} else if (result == SESSION_LOST_STATE_ERROR) {
LOGE("Session invalidated");
return kSessionStateLost;
} else if (result == LICENSE_REQUEST_NONCE_GENERATION_ERROR) {
LOGE("Nonce quota exceeded");
return kQuotaExceeded;
}
if (result != KEY_ADDED) {
return kResourceContention;
} else if (result != KEY_ADDED) {
LOGE("Unexpected Failure: GenerateKeyRequest() returned %lu", result);
return kUnexpectedError;
}
@@ -781,11 +926,16 @@ Cdm::Status CdmImpl::getKeyAllowedUsages(const std::string& session_id,
CdmResponseType result =
cdm_engine_->QueryKeyAllowedUsage(session_id, key_id, &usage_for_key);
if (result != NO_ERROR) {
// TODO(http://b/114435278): there are a million KEY_NOT_FOUND_* errors.
// that should probably all turn into kNoKey. Here, and below, and
// everywhere.
// TODO(b/114435278): There are multiple KEY_NOT_FOUND_* errors that should
// probably all turn into kNoKey. Here, and below, and everywhere.
if (result == KEY_NOT_FOUND_1) {
return kNoKey;
} else if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
} else if (result == SESSION_LOST_STATE_ERROR) {
LOGE("Session invalidated");
return kSessionStateLost;
} else {
LOGE("Unexpected error %d", result);
return kUnexpectedError;
@@ -809,6 +959,12 @@ Cdm::Status CdmImpl::getKeyAllowedUsages(const std::string& key_id,
if (result != NO_ERROR) {
if (result == KEY_NOT_FOUND_1 || result == KEY_NOT_FOUND_2) {
return kNoKey;
} else if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
} else if (result == SESSION_LOST_STATE_ERROR) {
LOGE("Session invalidated");
return kSessionStateLost;
} else if (result == KEY_CONFLICT_1) {
return kTypeError;
} else {
@@ -864,7 +1020,10 @@ Cdm::Status CdmImpl::close(const std::string& session_id) {
}
CdmResponseType result = cdm_engine_->CloseSession(session_id);
if (result != NO_ERROR) {
if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
} else if (result != NO_ERROR) {
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
@@ -903,11 +1062,16 @@ Cdm::Status CdmImpl::remove(const std::string& session_id) {
session_id, session_id, empty_initialization_data, kLicenseTypeRelease,
app_parameters_, &key_request);
if (result == LICENSE_REQUEST_NONCE_GENERATION_ERROR) {
if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
} else if (result == SESSION_LOST_STATE_ERROR) {
LOGE("Session invalidated");
return kSessionStateLost;
} else if (result == LICENSE_REQUEST_NONCE_GENERATION_ERROR) {
LOGE("Nonce quota exceeded");
return kQuotaExceeded;
}
if (result != KEY_MESSAGE) {
return kResourceContention;
} else if (result != KEY_MESSAGE) {
LOGE("Unexpected error %d", result);
cdm_engine_->CloseSession(session_id);
return kUnexpectedError;
@@ -937,7 +1101,13 @@ Cdm::Status CdmImpl::forceRemove(const std::string& session_id) {
CdmResponseType result = cdm_engine_->RemoveLicense(session_id);
if (result != NO_ERROR) {
if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
} else if (result == SESSION_LOST_STATE_ERROR) {
LOGE("Session invalidated");
return kSessionStateLost;
} else if (result != NO_ERROR) {
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
@@ -1003,6 +1173,21 @@ Cdm::Status CdmImpl::decrypt(const std::string& session_id,
return kSuccess;
}
if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
}
if (result == SESSION_LOST_STATE_ERROR) {
LOGE("Session invalidated");
return kSessionStateLost;
}
if (result == OUTPUT_TOO_LARGE_ERROR) {
LOGE("Output too large");
return kOutputTooLarge;
}
if (result == NEED_KEY || result == KEY_NOT_FOUND_3 ||
result == SESSION_NOT_FOUND_FOR_DECRYPT) {
LOGE("Key not available.");
@@ -1035,6 +1220,18 @@ Cdm::Status CdmImpl::genericEncrypt(const std::string& session_id,
if (result == NO_ERROR) {
return kSuccess;
}
if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
}
if (result == SESSION_LOST_STATE_ERROR) {
LOGE("Session invalidated");
return kSessionStateLost;
}
if (result == OUTPUT_TOO_LARGE_ERROR) {
LOGE("Output too large");
return kOutputTooLarge;
}
if (result == SESSION_NOT_FOUND_13) {
LOGE("No such session: %s", session_id.c_str());
return kSessionNotFound;
@@ -1064,6 +1261,18 @@ Cdm::Status CdmImpl::genericDecrypt(const std::string& session_id,
if (result == NO_ERROR) {
return kSuccess;
}
if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
}
if (result == SESSION_LOST_STATE_ERROR) {
LOGE("Session invalidated");
return kSessionStateLost;
}
if (result == OUTPUT_TOO_LARGE_ERROR) {
LOGE("Output too large");
return kOutputTooLarge;
}
if (result == SESSION_NOT_FOUND_14) {
LOGE("No such session: %s", session_id.c_str());
return kSessionNotFound;
@@ -1092,6 +1301,14 @@ Cdm::Status CdmImpl::genericSign(const std::string& session_id,
if (result == NO_ERROR) {
return kSuccess;
}
if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
}
if (result == SESSION_LOST_STATE_ERROR) {
LOGE("Session invalidated");
return kSessionStateLost;
}
if (result == SESSION_NOT_FOUND_15) {
LOGE("No such session: %s", session_id.c_str());
return kSessionNotFound;
@@ -1120,6 +1337,14 @@ Cdm::Status CdmImpl::genericVerify(const std::string& session_id,
if (result == NO_ERROR) {
return kSuccess;
}
if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
}
if (result == SESSION_LOST_STATE_ERROR) {
LOGE("Session invalidated");
return kSessionStateLost;
}
if (result == SESSION_NOT_FOUND_16) {
LOGE("No such session: %s", session_id.c_str());
return kSessionNotFound;
@@ -1276,13 +1501,6 @@ Cdm::Status CdmImpl::ConvertHdcpLevel(const std::string& query_value,
return kSuccess;
}
bool VerifyL1() {
metrics::CryptoMetrics throwaway_metrics;
std::unique_ptr<CryptoSession> cs(
CryptoSession::MakeCryptoSession(&throwaway_metrics));
return cs->GetSecurityLevel() == kSecurityLevelL1;
}
} // namespace
// static
@@ -1293,24 +1511,8 @@ Cdm::Status Cdm::initialize(SecureOutputType secure_output_type,
// 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.
switch (secure_output_type) {
case kOpaqueHandle:
// This output type requires an OEMCrypto that reports L1.
// This requirement comes from CryptoSession::SetDestinationBufferType().
if (!VerifyL1()) {
LOGE("Not an L1 implementation, kOpaqueHandle cannot be used!");
return kNotSupported;
}
break;
case kDirectRender:
case kNoSecureOutput:
break;

View File

@@ -620,11 +620,7 @@ TEST_F(CdmTest, Initialize) {
status =
Cdm::initialize(Cdm::kOpaqueHandle, working_client_info, g_host, g_host,
g_host, static_cast<Cdm::LogLevel>(g_cutoff));
if (wvoec::global_features.supports_level_1) {
EXPECT_EQ(Cdm::kSuccess, status);
} else {
EXPECT_EQ(Cdm::kNotSupported, status);
}
EXPECT_EQ(Cdm::kSuccess, status);
// One last init with everything correct and working.
status =
@@ -751,6 +747,27 @@ TEST_F(CdmTest, SetServiceCertificate) {
EXPECT_EQ(Cdm::kTypeError, status);
}
TEST_F(CdmTest, GetRobustnessLevel) {
Cdm::RobustnessLevel level;
Cdm::Status status = cdm_->getRobustnessLevel(&level);
ASSERT_EQ(Cdm::kSuccess, status);
LOGI("Got robustness level %d", level);
}
TEST_F(CdmTest, GetResourceRatingTier) {
uint32_t tier;
Cdm::Status status = cdm_->getResourceRatingTier(&tier);
ASSERT_EQ(Cdm::kSuccess, status);
LOGI("Got resource rating tier %lu", tier);
}
TEST_F(CdmTest, GetOemCryptoBuildInfo) {
std::string build_info;
Cdm::Status status = cdm_->getOemCryptoBuildInfo(&build_info);
ASSERT_EQ(Cdm::kSuccess, status);
LOGI("Got OEMCrypto build info: %s", build_info.c_str());
}
TEST_F(CdmTest, CreateSession) {
EnsureProvisioned();
@@ -1096,8 +1113,8 @@ TEST_F(CdmTest, PerOriginLoadPersistent) {
}
}
// TODO(b/34949512): Fix this test so it can be re-enabled.
TEST_F(CdmTest, DISABLED_LoadUsageRecord) {
TEST_F(CdmTest, LoadUsageRecord) {
EnsureProvisioned();
std::string session_id;
std::string response;
@@ -1133,6 +1150,7 @@ TEST_F(CdmTest, DISABLED_LoadUsageRecord) {
status = cdm_->close(session_id);
ASSERT_EQ(Cdm::kSuccess, status);
g_host->Reset();
EnsureProvisioned();
EXPECT_CALL(*this, onKeyStatusesChange(session_id, _)).Times(0);
EXPECT_CALL(*this, onMessage(session_id, Cdm::kLicenseRelease, _)).Times(0);
status = cdm_->load(session_id);
@@ -1140,8 +1158,8 @@ TEST_F(CdmTest, DISABLED_LoadUsageRecord) {
Mock::VerifyAndClear(this);
}
// TODO(b/109897011): Temporarily Disabled
TEST_F(CdmTest, DISABLED_DestroyUsageRecord) {
TEST_F(CdmTest, DestroyUsageRecord) {
EnsureProvisioned();
std::string session_id;
std::string response;
ASSERT_NO_FATAL_FAILURE(CreateSessionAndFetchLicense(
@@ -1175,8 +1193,8 @@ TEST_F(CdmTest, DISABLED_DestroyUsageRecord) {
Mock::VerifyAndClear(this);
}
// TODO(b/109897011): Temporarily Disabled
TEST_F(CdmTest, DISABLED_DestroyAllUsageRecords) {
TEST_F(CdmTest, DestroyAllUsageRecords) {
EnsureProvisioned();
std::string session_id;
std::string response;
ASSERT_NO_FATAL_FAILURE(CreateSessionAndFetchLicense(
@@ -1210,8 +1228,8 @@ TEST_F(CdmTest, DISABLED_DestroyAllUsageRecords) {
Mock::VerifyAndClear(this);
}
// TODO(b/109897011): Temporarily Disabled
TEST_F(CdmTest, DISABLED_ListUsageRecords) {
TEST_F(CdmTest, ListUsageRecords) {
EnsureProvisioned();
std::string session_id;
std::string response;
ASSERT_NO_FATAL_FAILURE(CreateSessionAndFetchLicense(
@@ -1474,6 +1492,125 @@ TEST_F(CdmTest, RemoveUsageRecord) {
ASSERT_EQ(Cdm::kSessionNotFound, status);
}
TEST_F(CdmTest, RemoveThreeUsageRecords) {
EnsureProvisioned();
std::string session_id1, session_id2, session_id3;
ASSERT_NO_FATAL_FAILURE(CreateSessionAndUpdate(Cdm::kPersistentUsageRecord,
Cdm::kCenc, &session_id1));
ASSERT_NO_FATAL_FAILURE(CreateSessionAndUpdate(Cdm::kPersistentUsageRecord,
Cdm::kCenc, &session_id2));
ASSERT_NO_FATAL_FAILURE(CreateSessionAndUpdate(Cdm::kPersistentUsageRecord,
Cdm::kCenc, &session_id3));
// Close, reload and remove the middle session first
EXPECT_EQ(Cdm::kSuccess, cdm_->close(session_id2));
EXPECT_CALL(*this, onKeyStatusesChange(session_id2, _)).Times(0);
EXPECT_CALL(*this, onMessage(session_id2, Cdm::kLicenseRelease, _)).Times(0);
Cdm::Status status = cdm_->load(session_id2);
EXPECT_EQ(Cdm::kSuccess, status);
Mock::VerifyAndClear(this);
// Remove the session. This causes a release message to be generated.
std::string message;
EXPECT_CALL(*this, onMessage(session_id2, Cdm::kLicenseRelease, _))
.WillOnce(SaveArg<2>(&message));
status = cdm_->remove(session_id2);
ASSERT_EQ(Cdm::kSuccess, status);
Mock::VerifyAndClear(this);
// The keys should already be unusable.
Cdm::KeyStatusMap map;
status = cdm_->getKeyStatuses(session_id2, &map);
ASSERT_EQ(Cdm::kSuccess, status);
EXPECT_TRUE(map.empty());
// Post the release message to the license server.
std::string response;
ASSERT_NO_FATAL_FAILURE(
FetchLicense(config_.license_server(), message, &response));
// Update the session.
EXPECT_CALL(*this, onRemoveComplete(session_id2));
status = updateWithRetry(session_id2, response);
ASSERT_EQ(Cdm::kSuccess, status);
Mock::VerifyAndClear(this);
// The session is now completely gone.
status = cdm_->close(session_id2);
ASSERT_EQ(Cdm::kSessionNotFound, status);
// Close, reload and remove the last session next
EXPECT_EQ(Cdm::kSuccess, cdm_->close(session_id3));
EXPECT_CALL(*this, onKeyStatusesChange(session_id3, _)).Times(0);
EXPECT_CALL(*this, onMessage(session_id3, Cdm::kLicenseRelease, _)).Times(0);
status = cdm_->load(session_id3);
EXPECT_EQ(Cdm::kSuccess, status);
Mock::VerifyAndClear(this);
// Remove the session. This causes a release message to be generated.
EXPECT_CALL(*this, onMessage(session_id3, Cdm::kLicenseRelease, _))
.WillOnce(SaveArg<2>(&message));
status = cdm_->remove(session_id3);
ASSERT_EQ(Cdm::kSuccess, status);
Mock::VerifyAndClear(this);
// The keys should already be unusable.
status = cdm_->getKeyStatuses(session_id3, &map);
ASSERT_EQ(Cdm::kSuccess, status);
EXPECT_TRUE(map.empty());
// Post the release message to the license server.
ASSERT_NO_FATAL_FAILURE(
FetchLicense(config_.license_server(), message, &response));
// Update the session.
EXPECT_CALL(*this, onRemoveComplete(session_id3));
status = updateWithRetry(session_id3, response);
ASSERT_EQ(Cdm::kSuccess, status);
Mock::VerifyAndClear(this);
// The session is now completely gone.
status = cdm_->close(session_id3);
ASSERT_EQ(Cdm::kSessionNotFound, status);
// Close, reload and remove the first session next
EXPECT_EQ(Cdm::kSuccess, cdm_->close(session_id1));
EXPECT_CALL(*this, onKeyStatusesChange(session_id1, _)).Times(0);
EXPECT_CALL(*this, onMessage(session_id1, Cdm::kLicenseRelease, _)).Times(0);
status = cdm_->load(session_id1);
EXPECT_EQ(Cdm::kSuccess, status);
Mock::VerifyAndClear(this);
// Remove the session. This causes a release message to be generated.
EXPECT_CALL(*this, onMessage(session_id1, Cdm::kLicenseRelease, _))
.WillOnce(SaveArg<2>(&message));
status = cdm_->remove(session_id1);
ASSERT_EQ(Cdm::kSuccess, status);
Mock::VerifyAndClear(this);
// The keys should already be unusable.
status = cdm_->getKeyStatuses(session_id1, &map);
ASSERT_EQ(Cdm::kSuccess, status);
EXPECT_TRUE(map.empty());
// Post the release message to the license server.
ASSERT_NO_FATAL_FAILURE(
FetchLicense(config_.license_server(), message, &response));
// Update the session.
EXPECT_CALL(*this, onRemoveComplete(session_id1));
status = updateWithRetry(session_id1, response);
ASSERT_EQ(Cdm::kSuccess, status);
Mock::VerifyAndClear(this);
// The session is now completely gone.
status = cdm_->close(session_id1);
ASSERT_EQ(Cdm::kSessionNotFound, status);
}
TEST_F(CdmTest, RemoveIncomplete) {
EnsureProvisioned();
std::string session_id;