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

@@ -325,8 +325,7 @@ class CdmEngine {
// data. Returns true if the metrics data is populated, false otherwise.
// |engine_metrics| is owned by the caller and must not be null.
// The CdmEngine implementation is a placeholder. Just return false.
virtual bool GetMetricsSnapshot(
__attribute__((unused)) drm_metrics::WvCdmMetrics *metrics) {
virtual bool GetMetricsSnapshot(drm_metrics::WvCdmMetrics* /* metrics */) {
return false;
}

View File

@@ -21,7 +21,7 @@ class EntitlementKeySession : public ContentKeySession {
metrics::CryptoMetrics* metrics);
~EntitlementKeySession() override {}
KeySessionType Type() { return kEntitlement; }
KeySessionType Type() override { return kEntitlement; }
// Load Keys for ContentKeySession
OEMCryptoResult LoadKeys(const std::string& message,

View File

@@ -17,6 +17,7 @@
namespace video_widevine {
class SignedMessage;
class LicenseRequest;
class VersionInfo;
} // namespace video_widevine
namespace wvcdm {
@@ -29,6 +30,7 @@ class CryptoKey;
using ::google::protobuf::RepeatedPtrField;
using video_widevine::License_KeyContainer;
using video_widevine::VersionInfo;
using video_widevine::WidevinePsshData_EntitledKey;
class CdmLicense {
@@ -75,6 +77,10 @@ class CdmLicense {
virtual bool is_offline() { return is_offline_; }
virtual const VersionInfo& GetServiceVersion() {
return latest_service_version_;
}
static bool ExtractProviderSessionToken(
const CdmKeyResponse& license_response,
std::string* provider_session_token);
@@ -152,6 +158,9 @@ class CdmLicense {
RepeatedPtrField<License_KeyContainer> entitlement_keys_;
std::string provider_client_token_;
// This is the latest version info extracted from the SignedMessage in
// HandleKeyResponse
VersionInfo latest_service_version_;
#if defined(UNIT_TEST)
friend class CdmLicenseTestPeer;

View File

@@ -68,7 +68,7 @@ class PolicyEngine {
virtual void SetLicenseForRelease(const video_widevine::License& license);
// Call this on first decrypt to set the start of playback.
virtual void BeginDecryption(void);
virtual bool BeginDecryption(void);
virtual void DecryptionEvent(void);
// UpdateLicense is used in handling a license response for a renewal request.

View File

@@ -83,6 +83,7 @@ class UsageTableHeader {
static int64_t GetRandomInRange(size_t upper_bound_exclusive);
static int64_t GetRandomInRangeWithExclusion(size_t upper_bound_exclusive,
size_t exclude);
size_t size() { return usage_entry_info_.size(); }
private:
CdmResponseType MoveEntry(uint32_t from /* usage entry number */,

View File

@@ -87,6 +87,7 @@ static const std::string QUERY_KEY_DECRYPT_HASH_SUPPORT =
static const std::string QUERY_VALUE_TRUE = "True";
static const std::string QUERY_VALUE_FALSE = "False";
static const std::string QUERY_VALUE_NONE = "None";
static const std::string QUERY_VALUE_STREAMING = "Streaming";
static const std::string QUERY_VALUE_OFFLINE = "Offline";
static const std::string QUERY_VALUE_SECURITY_LEVEL_L1 = "L1";

View File

@@ -631,7 +631,10 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
} else if (query_token == QUERY_KEY_CURRENT_SRM_VERSION) {
uint16_t current_srm_version;
status = crypto_session->GetSrmVersion(&current_srm_version);
if (status != NO_ERROR) {
if (status == NOT_IMPLEMENTED_ERROR) {
*query_response = QUERY_VALUE_NONE;
return NO_ERROR;
} else if (status != NO_ERROR) {
LOGW("CdmEngine::QueryStatus: GetCurrentSRMVersion failed: %d", status);
return status;
}
@@ -1098,24 +1101,7 @@ CdmResponseType CdmEngine::DeleteUsageRecord(const std::string& app_id,
return DELETE_USAGE_ERROR_2;
}
// Got provider token. Remove from OEMCrypto.
std::unique_ptr<CryptoSession> crypto_session(
CryptoSession::MakeCryptoSession(metrics_->GetCryptoMetrics()));
CdmResponseType status = crypto_session->Open(
security_level == kSecurityLevelL3 ? kLevel3 : kLevelDefault);
if (status == NO_ERROR) {
status = crypto_session->DeleteUsageInformation(provider_session_token);
}
if (status != NO_ERROR) {
LOGE("CdmEngine::DeleteUsageRecord: OEMCrypto failure");
}
// Remove from file system.
if (!handle.DeleteUsageInfo(app_id, provider_session_token)) {
LOGE("CdmEngine::DeleteUsageRecord: file system failure");
return DELETE_USAGE_ERROR_3;
}
return status;
return RemoveUsageInfo(app_id, provider_session_token);
}
CdmResponseType CdmEngine::GetOfflineLicenseState(
@@ -1389,129 +1375,98 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
}
CdmResponseType CdmEngine::RemoveAllUsageInfo(
const std::string& app_id, CdmSecurityLevel security_level) {
const std::string& app_id, CdmSecurityLevel cdm_security_level) {
LOGI("CdmEngine::RemoveAllUsageInfo: %s, security level: %d",
app_id.c_str(), security_level);
DeviceFiles handle(file_system_);
if (!handle.Init(security_level)) {
LOGE("CdmEngine::RemoveAllUsageInfo: unable to initialize device files");
return REMOVE_ALL_USAGE_INFO_ERROR_6;
}
std::vector<std::string> provider_session_tokens;
if (!handle.DeleteAllUsageInfoForApp(app_id, &provider_session_tokens)) {
LOGE("CdmEngine::RemoveAllUsageInfo: failed to delete usage records");
return REMOVE_ALL_USAGE_INFO_ERROR_7;
}
if (provider_session_tokens.size() == 0UL) {
return NO_ERROR;
}
// Got at least one provider token. Remove from OEMCrypto.
std::unique_ptr<CryptoSession> crypto_session(
CryptoSession::MakeCryptoSession(metrics_->GetCryptoMetrics()));
CdmResponseType status = crypto_session->Open(
security_level == kSecurityLevelL3 ? kLevel3 : kLevelDefault);
if (status == NO_ERROR) {
status = crypto_session->
DeleteMultipleUsageInformation(provider_session_tokens);
}
if (status != NO_ERROR) {
LOGE("CdmEngine::RemoveAllUsageInfo: CryptoSession failure");
}
return status;
}
CdmResponseType CdmEngine::RemoveAllUsageInfo(const std::string& app_id) {
LOGI("CdmEngine::RemoveAllUsageInfo: %s", app_id.c_str());
if (NULL == usage_property_set_.get()) {
app_id.c_str(), cdm_security_level);
if (usage_property_set_.get() == nullptr) {
usage_property_set_.reset(new UsagePropertySet());
}
usage_property_set_->set_app_id(app_id);
CdmResponseType status = NO_ERROR;
for (int j = kSecurityLevelL1; j < kSecurityLevelUnknown; ++j) {
DeviceFiles handle(file_system_);
if (handle.Init(static_cast<CdmSecurityLevel>(j))) {
SecurityLevel security_level =
static_cast<CdmSecurityLevel>(j) == kSecurityLevelL3
? kLevel3
: kLevelDefault;
usage_property_set_->set_security_level(security_level);
usage_session_.reset(new CdmSession(file_system_, metrics_->AddSession()));
usage_session_->Init(usage_property_set_.get());
DeviceFiles handle(file_system_);
if (handle.Init(cdm_security_level)) {
SecurityLevel security_level =
cdm_security_level == kSecurityLevelL3 ? kLevel3 : kLevelDefault;
usage_property_set_->set_security_level(security_level);
usage_session_.reset(new CdmSession(file_system_, metrics_->AddSession()));
usage_session_->Init(usage_property_set_.get());
switch (usage_session_->get_usage_support_type()) {
case kUsageEntrySupport: {
std::vector<DeviceFiles::CdmUsageData> usage_data;
// Retrieve all usage information but delete only one before
// refetching. This is because deleting the usage entry
// might cause other entries to be shifted and information updated.
do {
if (!handle.RetrieveUsageInfo(
DeviceFiles::GetUsageInfoFileName(app_id),
&usage_data)) {
LOGW("CdmEngine::RemoveAllUsageInfo: failed to retrieve usage info");
break;
}
if (usage_data.empty()) break;
CdmResponseType res = usage_session_->DeleteUsageEntry(
usage_data[0].usage_entry_number);
if (res != NO_ERROR) {
LOGW("CdmEngine::RemoveAllUsageInfo: failed to delete usage "
"entry: error: %d", res);
break;
}
if (!handle.DeleteUsageInfo(
DeviceFiles::GetUsageInfoFileName(app_id),
usage_data[0].provider_session_token)) {
LOGW("CdmEngine::RemoveAllUsageInfo: failed to delete usage "
"info");
break;
}
} while (!usage_data.empty());
std::vector<std::string> provider_session_tokens;
if (!handle.DeleteAllUsageInfoForApp(
switch (usage_session_->get_usage_support_type()) {
case kUsageEntrySupport: {
std::vector<DeviceFiles::CdmUsageData> usage_data;
// Retrieve all usage information but delete only one before
// refetching. This is because deleting the usage entry
// might cause other entries to be shifted and information updated.
do {
if (!handle.RetrieveUsageInfo(
DeviceFiles::GetUsageInfoFileName(app_id),
&provider_session_tokens)) {
status = REMOVE_ALL_USAGE_INFO_ERROR_5;
&usage_data)) {
LOGW("CdmEngine::RemoveAllUsageInfo: failed to retrieve usage info");
break;
}
break;
}
case kUsageTableSupport: {
std::vector<std::string> provider_session_tokens;
if (!handle.DeleteAllUsageInfoForApp(
if (usage_data.empty()) break;
CdmResponseType res = usage_session_->DeleteUsageEntry(
usage_data[0].usage_entry_number);
if (res != NO_ERROR) {
LOGW("CdmEngine::RemoveAllUsageInfo: failed to delete usage "
"entry: error: %d", res);
break;
}
if (!handle.DeleteUsageInfo(
DeviceFiles::GetUsageInfoFileName(app_id),
&provider_session_tokens)) {
LOGE("CdmEngine::RemoveAllUsageInfo: failed to delete %d secure"
"stops", j);
status = REMOVE_ALL_USAGE_INFO_ERROR_1;
} else {
CdmResponseType status2 = usage_session_->
DeleteMultipleUsageInformation(provider_session_tokens);
if (status2 != NO_ERROR) status = status2;
usage_data[0].provider_session_token)) {
LOGW("CdmEngine::RemoveAllUsageInfo: failed to delete usage "
"info");
break;
}
break;
} while (!usage_data.empty());
std::vector<std::string> provider_session_tokens;
if (!handle.DeleteAllUsageInfoForApp(
DeviceFiles::GetUsageInfoFileName(app_id),
&provider_session_tokens)) {
status = REMOVE_ALL_USAGE_INFO_ERROR_5;
}
default:
// Ignore
break;
break;
}
} else {
LOGE("CdmEngine::RemoveAllUsageInfo: failed to initialize L%d device"
"files", j);
status = REMOVE_ALL_USAGE_INFO_ERROR_2;
case kUsageTableSupport: {
std::vector<std::string> provider_session_tokens;
if (!handle.DeleteAllUsageInfoForApp(
DeviceFiles::GetUsageInfoFileName(app_id),
&provider_session_tokens)) {
LOGE("CdmEngine::RemoveAllUsageInfo: failed to delete %d secure"
"stops", cdm_security_level);
status = REMOVE_ALL_USAGE_INFO_ERROR_1;
} else {
CdmResponseType status2 = usage_session_->
DeleteMultipleUsageInformation(provider_session_tokens);
if (status2 != NO_ERROR) status = status2;
}
break;
}
default:
// Ignore
break;
}
}
usage_session_.reset(NULL);
return status;
}
CdmResponseType CdmEngine::RemoveAllUsageInfo(const std::string& app_id) {
LOGI("CdmEngine::RemoveAllUsageInfo: %s", app_id.c_str());
CdmResponseType status_l1, status_l3;
status_l1 = status_l3 = NO_ERROR;
status_l1 = RemoveAllUsageInfo(app_id, kSecurityLevelL1);
status_l3 = RemoveAllUsageInfo(app_id, kSecurityLevelL3);
return (status_l3 == NO_ERROR) ? status_l3 : status_l1;
}
CdmResponseType CdmEngine::RemoveUsageInfo(
const std::string& app_id,
const CdmSecureStopId& provider_session_token) {
@@ -1653,6 +1608,7 @@ CdmResponseType CdmEngine::LoadUsageSession(const CdmKeySetId& key_set_id,
}
int error_detail = NO_ERROR;
usage_data.key_set_id = key_set_id;
CdmResponseType status = session->RestoreUsageSession(usage_data,
&error_detail);
session->GetMetrics()->cdm_session_restore_usage_session_.Increment(
@@ -1854,7 +1810,7 @@ CdmResponseType CdmEngine::ParseDecryptHashString(
return INVALID_DECRYPT_HASH_FORMAT;
}
std::vector<uint8_t> hash_vec = wvcdm::Base64Decode(tokens[2]);
std::vector<uint8_t> hash_vec = wvcdm::a2b_hex(tokens[2]);
if (hash_vec.empty()) {
LOGE("CdmEngine::ParseDecryptHashString: malformed hash: %s",
hash_string.c_str());

View File

@@ -267,6 +267,7 @@ CdmResponseType CdmSession::RestoreOfflineSession(
} else {
CdmResponseType sts = usage_table_header_->LoadEntry(
crypto_session_.get(), usage_entry_, usage_entry_number_);
crypto_metrics_->usage_table_header_load_entry_.Increment(sts);
if (sts != NO_ERROR) {
LOGE(
"CdmSession::RestoreOfflineSession: failed to load usage entry = "
@@ -341,6 +342,7 @@ CdmResponseType CdmSession::RestoreUsageSession(
usage_table_header_ != NULL) {
sts = usage_table_header_->LoadEntry(
crypto_session_.get(), usage_entry_, usage_entry_number_);
crypto_metrics_->usage_table_header_load_entry_.Increment(sts);
if (sts != NO_ERROR) {
LOGE("CdmSession::RestoreUsageSession: failed to load usage entry = %d",
sts);
@@ -504,17 +506,24 @@ CdmResponseType CdmSession::AddKeyInternal(const CdmKeyResponse& key_response) {
sts = usage_table_header_->AddEntry(
crypto_session_.get(), is_offline_, key_set_id_,
DeviceFiles::GetUsageInfoFileName(app_id), &usage_entry_number_);
crypto_metrics_->usage_table_header_add_entry_.Increment(sts);
if (sts != NO_ERROR) return sts;
}
}
sts = license_parser_->HandleKeyResponse(key_response);
// Update the license sdk and service versions.
const VersionInfo& version_info = license_parser_->GetServiceVersion();
metrics_->license_sdk_version_.Record(version_info.license_sdk_version());
metrics_->license_sdk_version_.Record(version_info.license_service_version());
// Update or delete entry if usage table header+entries are supported
if (usage_support_type_ == kUsageEntrySupport &&
!provider_session_token.empty() && usage_table_header_ != NULL) {
if (sts != KEY_ADDED) {
CdmResponseType delete_sts = usage_table_header_->DeleteEntry(
usage_entry_number_, file_handle_.get(), crypto_metrics_);
crypto_metrics_->usage_table_header_delete_entry_.Increment(delete_sts);
if (delete_sts != NO_ERROR) {
LOGW("CdmSession::AddKey: Delete usage entry failed = %d",
delete_sts);
@@ -629,8 +638,7 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) {
if (status == NO_ERROR) {
if (is_initial_decryption_) {
policy_engine_->BeginDecryption();
is_initial_decryption_ = false;
is_initial_decryption_ = !policy_engine_->BeginDecryption();
}
has_decrypted_since_last_report_ = true;
if (!is_usage_update_needed_) {
@@ -712,6 +720,7 @@ CdmResponseType CdmSession::GenerateReleaseRequest(CdmKeyRequest* key_request) {
usage_support_type_ == kUsageEntrySupport) {
status =
usage_table_header_->UpdateEntry(crypto_session_.get(), &usage_entry_);
if (status != NO_ERROR) {
LOGE(
"CdmSession::GenerateReleaseRequest: Update usage entry failed = "
@@ -765,6 +774,7 @@ CdmResponseType CdmSession::DeleteUsageEntry(uint32_t usage_entry_number) {
// The usage entry cannot be deleted if it has a crypto session handling
// it, so close and reopen session.
UpdateUsageEntryInformation();
CdmResponseType sts;
crypto_session_->Close();
crypto_session_.reset(CryptoSession::MakeCryptoSession(crypto_metrics_));
@@ -785,8 +795,10 @@ CdmResponseType CdmSession::DeleteUsageEntry(uint32_t usage_entry_number) {
return INCORRECT_USAGE_SUPPORT_TYPE_1;
}
return usage_table_header_->DeleteEntry(usage_entry_number,
file_handle_.get(), crypto_metrics_);
sts = usage_table_header_->DeleteEntry(usage_entry_number,
file_handle_.get(), crypto_metrics_);
crypto_metrics_->usage_table_header_delete_entry_.Increment(sts);
return sts;
}
bool CdmSession::IsKeyLoaded(const KeyId& key_id) {
@@ -924,14 +936,14 @@ CdmResponseType CdmSession::RemoveKeys() {
}
CdmResponseType CdmSession::RemoveLicense() {
CdmResponseType sts = NO_ERROR;
if (is_offline_ || has_provider_session_token()) {
DeleteLicenseFile();
if (usage_support_type_ == kUsageEntrySupport &&
has_provider_session_token()) {
CdmResponseType sts = DeleteUsageEntry(usage_entry_number_);
if (NO_ERROR != sts) return sts;
sts = DeleteUsageEntry(usage_entry_number_);
}
DeleteLicenseFile();
}
return NO_ERROR;
}
@@ -1025,12 +1037,12 @@ CdmResponseType CdmSession::UpdateUsageEntryInformation() {
}
CdmResponseType sts = NO_ERROR;
M_TIME(
sts = usage_table_header_->UpdateEntry(crypto_session_.get(),
&usage_entry_),
crypto_metrics_,
crypto_session_update_usage_entry_,
sts);
// TODO(blueeyes): Add measurements to all UpdateEntry calls in a way that
// allos us to isolate this particular use case within
// UpdateUsageEntryInformation.
M_TIME(sts = usage_table_header_->UpdateEntry(crypto_session_.get(),
&usage_entry_),
crypto_metrics_, usage_table_header_update_entry_, sts);
if (sts != NO_ERROR) return sts;

View File

@@ -227,8 +227,7 @@ void CryptoSession::Init() {
sts = OEMCrypto_SetSandbox(
reinterpret_cast<const uint8_t*>(sandbox_id.c_str()),
sandbox_id.length());
// TODO(blueeyes): it might be worth saving the sandbox id in a
// metric.
metrics_->oemcrypto_set_sandbox_.Record(sandbox_id);
}
M_TIME(sts = OEMCrypto_Initialize(), metrics_, oemcrypto_initialize_,
sts);
@@ -736,6 +735,7 @@ CdmResponseType CryptoSession::Open(SecurityLevel requested_security_level) {
}
}
usage_table_header_ = *header;
metrics_->usage_table_header_initial_size_.Record((*header)->size());
}
}
}
@@ -1499,11 +1499,7 @@ CdmResponseType CryptoSession::GenerateNonce(uint32_t* nonce) {
bool CryptoSession::SetDestinationBufferType() {
if (Properties::oem_crypto_use_secure_buffers()) {
if (GetSecurityLevel() == kSecurityLevelL1) {
destination_buffer_type_ = OEMCrypto_BufferType_Secure;
} else {
destination_buffer_type_ = OEMCrypto_BufferType_Clear;
}
destination_buffer_type_ = OEMCrypto_BufferType_Secure;
} else if (Properties::oem_crypto_use_fifo()) {
destination_buffer_type_ = OEMCrypto_BufferType_Direct;
} else if (Properties::oem_crypto_use_userspace_buffers()) {
@@ -1812,6 +1808,14 @@ CdmResponseType CryptoSession::GetSrmVersion(uint16_t* srm_version) {
status = OEMCrypto_GetCurrentSRMVersion(srm_version);
});
// SRM is an optional feature. Whether it is implemented is up to the
// discretion of OEMs
if (status == OEMCrypto_ERROR_NOT_IMPLEMENTED) {
LOGV("CryptoSession::GetSrmVersion: OEMCrypto_GetCurrentSRMVersion not "
"implemented");
return NOT_IMPLEMENTED_ERROR;
}
return MapOEMCryptoResult(
status, GET_SRM_VERSION_ERROR, "GetCurrentSRMVersion");
}
@@ -1863,6 +1867,7 @@ bool CryptoSession::GetResourceRatingTier(SecurityLevel security_level,
}
WithOecReadLock("GetResourceRatingTier", [&] {
*tier = OEMCrypto_ResourceRatingTier(security_level);
metrics_->oemcrypto_resource_rating_tier_.Record(*tier);
});
if (*tier < RESOURCE_RATING_TIER_LOW || *tier > RESOURCE_RATING_TIER_HIGH) {
uint32_t api_version;
@@ -1933,6 +1938,7 @@ CdmResponseType CryptoSession::SetDecryptHash(
sts = OEMCrypto_SetDecryptHash(
oec_session_id_, frame_number,
reinterpret_cast<const uint8_t*>(hash.data()), hash.size());
metrics_->oemcrypto_set_decrypt_hash_.Increment(sts);
});
return MapOEMCryptoResult(sts, SET_DECRYPT_HASH_ERROR, "SetDecryptHash");
@@ -1946,7 +1952,7 @@ CdmResponseType CryptoSession::GetDecryptHashError(std::string* error_string) {
}
error_string->clear();
uint32_t failed_frame_number;
uint32_t failed_frame_number = 0;
OEMCryptoResult sts;
WithOecSessionLock("GetDecryptHashError", [&] {
sts = OEMCrypto_GetHashErrorCode(oec_session_id_, &failed_frame_number);
@@ -2274,6 +2280,7 @@ CdmResponseType CryptoSession::CreateUsageTableHeader(
reinterpret_cast<uint8_t*>(
const_cast<char*>(usage_table_header->data())),
&usage_table_header_size);
metrics_->oemcrypto_create_usage_table_header_.Increment(result);
});
if (result == OEMCrypto_ERROR_SHORT_BUFFER) {
@@ -2284,6 +2291,7 @@ CdmResponseType CryptoSession::CreateUsageTableHeader(
reinterpret_cast<uint8_t*>(
const_cast<char*>(usage_table_header->data())),
&usage_table_header_size);
metrics_->oemcrypto_create_usage_table_header_.Increment(result);
});
}
@@ -2305,6 +2313,7 @@ CdmResponseType CryptoSession::LoadUsageTableHeader(
requested_security_level_,
reinterpret_cast<const uint8_t*>(usage_table_header.data()),
usage_table_header.size());
metrics_->oemcrypto_load_usage_table_header_.Increment(result);
});
if (result != OEMCrypto_SUCCESS) {
@@ -2347,6 +2356,7 @@ CdmResponseType CryptoSession::CreateUsageEntry(uint32_t* entry_number) {
OEMCryptoResult result;
WithOecWriteLock("CreateUsageEntry", [&] {
result = OEMCrypto_CreateNewUsageEntry(oec_session_id_, entry_number);
metrics_->oemcrypto_create_new_usage_entry_.Increment(result);
});
if (result != OEMCrypto_SUCCESS) {
@@ -2377,13 +2387,14 @@ CdmResponseType CryptoSession::LoadUsageEntry(
oec_session_id_, entry_number,
reinterpret_cast<const uint8_t*>(usage_entry.data()),
usage_entry.size());
metrics_->oemcrypto_load_usage_entry_.Increment(result);
});
if (result != OEMCrypto_SUCCESS) {
if (result == OEMCrypto_WARNING_GENERATION_SKEW) {
LOGW("LoadUsageEntry: OEMCrypto_LoadUsageEntry warning: generation skew");
} else {
LOGE("LoadUsageTableHeader: OEMCrypto_LoadUsageEntry error: %d", result);
LOGE("LoadUsageEntry: OEMCrypto_LoadUsageEntry error: %d", result);
}
}
@@ -2469,6 +2480,7 @@ CdmResponseType CryptoSession::ShrinkUsageTableHeader(
result = OEMCrypto_ShrinkUsageTableHeader(
requested_security_level_, new_entry_count, NULL,
&usage_table_header_len);
metrics_->oemcrypto_shrink_usage_table_header_.Increment(result);
});
if (result == OEMCrypto_ERROR_SHORT_BUFFER) {
@@ -2480,6 +2492,7 @@ CdmResponseType CryptoSession::ShrinkUsageTableHeader(
reinterpret_cast<uint8_t*>(
const_cast<char*>(usage_table_header->data())),
&usage_table_header_len);
metrics_->oemcrypto_shrink_usage_table_header_.Increment(result);
});
}
@@ -2497,6 +2510,7 @@ CdmResponseType CryptoSession::MoveUsageEntry(uint32_t new_entry_number) {
OEMCryptoResult result;
WithOecWriteLock("MoveUsageEntry", [&] {
result = OEMCrypto_MoveEntry(oec_session_id_, new_entry_number);
metrics_->oemcrypto_move_entry_.Increment(result);
});
return MapOEMCryptoResult(
@@ -2545,6 +2559,7 @@ bool CryptoSession::CreateOldUsageEntry(
reinterpret_cast<uint8_t*>(const_cast<char*>(client_mac_key.data())),
reinterpret_cast<const uint8_t*>(provider_session_token.data()),
provider_session_token.size());
metrics_->oemcrypto_create_old_usage_entry_.Increment(result);
});
if (result != OEMCrypto_SUCCESS) {
@@ -2565,6 +2580,7 @@ CdmResponseType CryptoSession::CopyOldUsageEntry(
oec_session_id_,
reinterpret_cast<const uint8_t*>(provider_session_token.data()),
provider_session_token.size());
metrics_->oemcrypto_copy_old_usage_entry_.Increment(result);
});
return MapOEMCryptoResult(

View File

@@ -69,6 +69,7 @@ OEMCryptoResult EntitlementKeySession::SelectKey(const std::string& key_id,
message.size(), 1, &entitled_key),
metrics_, oemcrypto_load_entitled_keys_, result);
if (result != OEMCrypto_SUCCESS) {
LOGE("SelectKey: OEMCrypto_LoadEntitledContentKeys error=%d", result);
return result;
}

View File

@@ -514,6 +514,9 @@ CdmResponseType CdmLicense::HandleKeyResponse(
LOGE("CdmLicense::HandleKeyResponse: not initialized");
return LICENSE_PARSER_NOT_INITIALIZED_2;
}
// Clear the latest service version when we receive a new response.
latest_service_version_.Clear();
if (license_response.empty()) {
LOGE("CdmLicense::HandleKeyResponse: empty license response");
return EMPTY_LICENSE_RESPONSE_1;
@@ -527,6 +530,8 @@ CdmResponseType CdmLicense::HandleKeyResponse(
return INVALID_LICENSE_RESPONSE;
}
latest_service_version_ = signed_response.service_version_info();
if (use_privacy_mode_ && Properties::allow_service_certificate_requests() &&
signed_response.type() == SignedMessage::SERVICE_CERTIFICATE) {
std::string signed_certificate;
@@ -607,7 +612,7 @@ CdmResponseType CdmLicense::HandleKeyResponse(
provider_client_token_ = license.provider_client_token();
if (license.has_srm_update()) {
CdmResponseType status = crypto_session_->LoadSrm(license.srm_update());
status = crypto_session_->LoadSrm(license.srm_update());
switch (status) {
case NO_ERROR:
break;

View File

@@ -390,6 +390,13 @@ message RemoteAttestation {
optional bytes signature = 3;
}
message VersionInfo {
// License SDK version reported by the Widevine License SDK.
optional string license_sdk_version = 1;
// Version of the service hosting the license SDK.
optional string license_service_version = 2;
}
message SignedMessage {
enum MessageType {
LICENSE_REQUEST = 1;
@@ -412,6 +419,9 @@ message SignedMessage {
optional RemoteAttestation remote_attestation = 5;
repeated MetricData metric_data = 6;
// Version information from the SDK and license service. This information is
// provided in the license response.
optional VersionInfo service_version_info = 7;
}
message GroupKeys {
enum GroupLicenseVersion {

View File

@@ -119,19 +119,19 @@ OEMCryptoResult OEMCrypto_CreateOldUsageEntry(SecurityLevel level,
pst_length);
}
uint32_t OEMCrypto_GetAnalogOutputFlags(SecurityLevel level) {
uint32_t OEMCrypto_GetAnalogOutputFlags(SecurityLevel) {
return ::OEMCrypto_GetAnalogOutputFlags();
}
const char* OEMCrypto_BuildInformation(SecurityLevel level) {
const char* OEMCrypto_BuildInformation(SecurityLevel) {
return ::OEMCrypto_BuildInformation();
}
uint32_t OEMCrypto_ResourceRatingTier(SecurityLevel level) {
uint32_t OEMCrypto_ResourceRatingTier(SecurityLevel) {
return ::OEMCrypto_ResourceRatingTier();
}
uint32_t OEMCrypto_SupportsDecryptHash(SecurityLevel level) {
uint32_t OEMCrypto_SupportsDecryptHash(SecurityLevel) {
return ::OEMCrypto_SupportsDecryptHash();
}
@@ -141,7 +141,7 @@ extern "C" OEMCryptoResult OEMCrypto_LoadKeys_Back_Compat(
OEMCrypto_Substring enc_mac_keys_iv, OEMCrypto_Substring enc_mac_keys,
size_t num_keys, const OEMCrypto_KeyObject* key_array,
OEMCrypto_Substring pst, OEMCrypto_Substring srm_restriction_data,
OEMCrypto_LicenseType license_type, OEMCryptoCipherMode* cipher_modes) {
OEMCrypto_LicenseType license_type, OEMCryptoCipherMode*) {
// TODO(b/72223802): make this backwards compatibile for versions < 14.
return OEMCrypto_LoadKeys(session, message, message_length, signature,
signature_length, enc_mac_keys_iv, enc_mac_keys,

View File

@@ -231,7 +231,7 @@ void PolicyEngine::UpdateLicense(const License& license) {
NotifyExpirationUpdate(current_time);
}
void PolicyEngine::BeginDecryption() {
bool PolicyEngine::BeginDecryption() {
if (playback_start_time_ == 0) {
switch (license_state_) {
case kLicenseStateCanPlay:
@@ -246,14 +246,17 @@ void PolicyEngine::BeginDecryption() {
license_state_ = kLicenseStateNeedRenewal;
}
NotifyExpirationUpdate(playback_start_time_);
break;
return true;
case kLicenseStateInitial:
case kLicenseStatePending:
case kLicenseStateExpired:
default:
break;
return false;
}
}
else {
return true;
}
}
void PolicyEngine::DecryptionEvent() { last_playback_time_ = GetCurrentTime(); }

View File

@@ -500,7 +500,8 @@ CdmResponseType UsageTableHeader::StoreEntry(uint32_t usage_entry_number,
CdmResponseType UsageTableHeader::Shrink(
metrics::CryptoMetrics* metrics,
uint32_t number_of_usage_entries_to_delete) {
LOGI("UsageTableHeader::Shrink: %d", number_of_usage_entries_to_delete);
LOGI("UsageTableHeader::Shrink: %d (of %d)",
number_of_usage_entries_to_delete, usage_entry_info_.size());
if (usage_entry_info_.empty()) {
LOGE("UsageTableHeader::Shrink: usage entry info table unexpectedly empty");
return NO_USAGE_ENTRIES;

View File

@@ -405,10 +405,10 @@ TEST_F(WvCdmEngineTest, ParseDecryptHashStringTest) {
const std::string test_frame_number_string =
std::to_string(test_frame_number);
const std::string test_invalid_hash = "an invalid hash";
std::vector<uint8_t> binary_hash{ 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38 };
std::vector<uint8_t> binary_hash{ 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0xFF };
const std::string test_valid_decoded_hash(binary_hash.begin(),
binary_hash.end());
const std::string test_valid_hash = Base64Encode(binary_hash);
const std::string test_valid_hash = b2a_hex(binary_hash);
const std::string test_invalid_hash_string = "sample hash string";
const std::string test_valid_hash_string = test_session_id + kComma +
test_frame_number_string + kComma + test_valid_hash;

View File

@@ -361,7 +361,7 @@ TEST_F(CdmSessionTest, UpdateUsageEntry) {
std::string serialized_metrics;
ASSERT_TRUE(metrics.SerializeToString(&serialized_metrics));
EXPECT_GT(metrics.crypto_metrics()
.crypto_session_update_usage_entry_time_us().size(), 0)
.usage_table_header_update_entry_time_us().size(), 0)
<< "Missing update usage entry metric. Metrics: "
<< wvcdm::b2a_hex(serialized_metrics);
}

View File

@@ -819,6 +819,40 @@ TEST_F(PolicyEngineTest, PlaybackOk_RentalAndLicense0_WithPlayback) {
EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId));
}
TEST_F(PolicyEngineTest,
PlaybackOk_RentalAndLicense0_WithPlaybackBeforeLicense) {
License_Policy* policy = license_.mutable_policy();
policy->clear_license_duration_seconds();
policy->clear_rental_duration_seconds();
// Only |playback_duration_seconds| set.
policy_engine_->BeginDecryption();
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(kLicenseStartTime + 1))
.WillOnce(Return(kPlaybackStartTime))
.WillOnce(Return(kPlaybackStartTime + kPlaybackDuration - 10))
.WillOnce(Return(kPlaybackStartTime + kPlaybackDuration + 10));
ExpectSessionKeysChange(kKeyStatusExpired, false);
ExpectSessionKeysChange(kKeyStatusUsable, true);
EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(_, 0));
EXPECT_CALL(mock_event_listener_,
OnExpirationUpdate(_, kPlaybackStartTime + kPlaybackDuration));
policy_engine_->SetLicense(license_);
policy_engine_->BeginDecryption();
policy_engine_->OnTimerEvent();
EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId));
EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId));
policy_engine_->OnTimerEvent();
EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId));
EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId));
}
TEST_F(PolicyEngineTest, PlaybackOk_Durations0) {
License_Policy* policy = license_.mutable_policy();
policy->set_rental_duration_seconds(kDurationUnlimited);