Merge "Suppress error for removing lingering offline licenses." into rvc-dev am: 792e17a8c4 am: d7f5a74ea3

Change-Id: Idb87851939277a1950b0ad5605c48a772ae56a6d
This commit is contained in:
Alex Dale
2020-05-01 00:24:13 +00:00
committed by Automerger Merge Worker
9 changed files with 160 additions and 7 deletions

View File

@@ -233,6 +233,12 @@ class CdmSession {
virtual CdmResponseType AddKeyInternal(const CdmKeyResponse& key_response);
void UpdateRequestLatencyTiming(CdmResponseType sts);
// Checks that the usage entry in the usage table header matches the
// information of the currently loaded license for this session.
// Returns false if there is any unexpected mismatch of information,
// true otherwise.
bool VerifyOfflineUsageEntry();
// These setters are for testing only. Takes ownership of the pointers.
void set_license_parser(CdmLicense* license_parser);
void set_crypto_session(CryptoSession* crypto_session);

View File

@@ -411,6 +411,7 @@ enum CdmResponseType {
LOAD_USAGE_ENTRY_INVALID_SESSION = 357,
MOVE_USAGE_ENTRY_DESTINATION_IN_USE = 358,
SHRINK_USAGE_TABLE_HEADER_ENTRY_IN_USE = 359,
LICENSE_USAGE_ENTRY_MISSING = 360,
// Don't forget to add new values to
// * core/test/test_printers.cpp.
// * android/include/mapErrors-inl.h

View File

@@ -169,10 +169,13 @@ CdmResponseType CdmEngine::OpenKeySetSession(
key_set_in_use =
release_key_sets_.find(key_set_id) != release_key_sets_.end();
}
if (key_set_in_use) CloseKeySetSession(key_set_id);
if (key_set_in_use) {
LOGD("Reopening existing key session");
CloseKeySetSession(key_set_id);
}
CdmSessionId session_id;
CdmResponseType sts =
const CdmResponseType sts =
OpenSession(KEY_SYSTEM, property_set, event_listener,
nullptr /* forced_session_id */, &session_id);
@@ -1114,14 +1117,19 @@ CdmResponseType CdmEngine::RemoveOfflineLicense(
property_set.set_security_level(
security_level == kSecurityLevelL3 ? kLevel3 : kLevelDefault);
DeviceFiles handle(file_system_);
if (!handle.Init(security_level)) {
LOGE("Cannot initialize device files: security_level = %s",
security_level == kSecurityLevelL3 ? "L3" : "Default");
return REMOVE_OFFLINE_LICENSE_ERROR_1;
}
CdmResponseType sts = OpenKeySetSession(key_set_id, &property_set,
nullptr /* event listener */);
if (sts != NO_ERROR) {
if (!handle.Init(security_level)) {
LOGE("Cannot initialize device files");
}
LOGE("Failed to open key set session: status = %d", static_cast<int>(sts));
handle.DeleteLicense(key_set_id);
return REMOVE_OFFLINE_LICENSE_ERROR_1;
return sts;
}
CdmSessionId session_id;
@@ -1141,6 +1149,14 @@ CdmResponseType CdmEngine::RemoveOfflineLicense(
session_id = iter->second.first;
sts = RemoveLicense(session_id);
}
} else if (sts == LICENSE_USAGE_ENTRY_MISSING) {
// It is possible that the CDM is tracking a key set ID, but has
// removed the usage information associated with it. In this case,
// it will no longer be possible to load the license for release;
// and the file should simply be deleted.
LOGW("License usage entry is missing, deleting license file");
handle.DeleteLicense(key_set_id);
sts = NO_ERROR;
}
if (sts != NO_ERROR) {

View File

@@ -282,6 +282,9 @@ CdmResponseType CdmSession::RestoreOfflineSession(const CdmKeySetId& key_set_id,
key_response_, &provider_session_token) ||
usage_table_header_ == nullptr) {
provider_session_token.clear();
} else if (!VerifyOfflineUsageEntry()) {
LOGE("License usage entry is invalid, cannot restore");
return LICENSE_USAGE_ENTRY_MISSING;
} else {
CdmResponseType sts = usage_table_header_->LoadEntry(
crypto_session_.get(), usage_entry_, usage_entry_number_);
@@ -1131,6 +1134,25 @@ void CdmSession::UpdateRequestLatencyTiming(CdmResponseType sts) {
license_request_latency_.Clear();
}
bool CdmSession::VerifyOfflineUsageEntry() {
// Check that the current license is the same as the expected
// entry in the usage table. It is possible that the license has
// been removed from the usage table but the license file remains.
if (usage_entry_number_ >= usage_table_header_->size()) {
LOGD("License usage entry does not exist: entry_number = %u, size = %zu",
usage_entry_number_, usage_table_header_->size());
return false;
}
const CdmUsageEntryInfo& usage_entry_info =
usage_table_header_->usage_entry_info().at(usage_entry_number_);
if (usage_entry_info.storage_type != kStorageLicense ||
usage_entry_info.key_set_id != key_set_id_) {
LOGD("License usage entry does not match");
return false;
}
return true;
}
// For testing only - takes ownership of pointers
void CdmSession::set_license_parser(CdmLicense* license_parser) {

View File

@@ -521,6 +521,9 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
case LICENSE_RESPONSE_PARSE_ERROR_5:
*os << "LICENSE_RESPONSE_PARSE_ERROR_5";
break;
case LICENSE_USAGE_ENTRY_MISSING:
*os << "LICENSE_USAGE_ENTRY_MISSING";
break;
case LIST_LICENSE_ERROR_1:
*os << "LIST_LICENSE_ERROR_1";
break;

View File

@@ -5997,6 +5997,107 @@ TEST_F(WvCdmRequestLicenseTest, DISABLED_DecryptPathTest) {
decryptor_->CloseSession(session_id_);
}
// This tests checks that if a valid offline license file is found on
// the device but is missing the usage entry associated with it, that
// the CDM can still remove the license without issuing an error back
// to the calling app.
// This checks two cases:
// 1) The license's entry is outside the range of the table
// 2) The entry in the usage table that the license points to does
// not match the license's key set ID (possible for entry to have
// been overwritten).
TEST_F(WvCdmRequestLicenseTest, RemoveOfflineLicenseWithMissingUsageEntry) {
Unprovision();
Provision();
const CdmSecurityLevel security_level = GetDefaultSecurityLevel();
FileSystem file_system;
DeviceFiles handle(&file_system);
EXPECT_TRUE(handle.Init(security_level));
std::string key_id;
std::string client_auth;
GetOfflineConfiguration(&key_id, &client_auth);
// Setup: Request an offline license to create a valid license file.
// This license will be used to test how the CDM handles request to
// remove it when its entry has been deleted.
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
nullptr, &session_id_);
GenerateKeyRequest(key_id, kLicenseTypeOffline);
VerifyKeyRequestResponse(config_.license_server(), client_auth);
// Save the key set ID for check below.
const std::string original_key_set_id(key_set_id_);
decryptor_->CloseSession(session_id_);
// Retrieve license from storage for later.
DeviceFiles::CdmLicenseData license_data;
DeviceFiles::ResponseType sub_result = DeviceFiles::kNoError;
EXPECT_TRUE(
handle.RetrieveLicense(original_key_set_id, &license_data, &sub_result));
EXPECT_EQ(DeviceFiles::kNoError, sub_result);
// Re-provision.
Unprovision();
handle.DeleteAllFiles();
Provision();
// Part 1: Test when usage entry is out of range of the table.
// Store license from earlier, this will cause ListStoredLicenses() to
// return the key set ID of the setup license.
EXPECT_TRUE(handle.StoreLicense(license_data, &sub_result));
EXPECT_EQ(DeviceFiles::kNoError, sub_result);
std::vector<CdmSecureStopId> key_set_ids;
EXPECT_EQ(NO_ERROR, decryptor_->ListStoredLicenses(
security_level, kDefaultCdmIdentifier, &key_set_ids));
// Note: It is possible that future changes to the CDM will cause this
// check to fail (such by filtering results from ListStoreLicenses).
EXPECT_THAT(key_set_ids, Contains(original_key_set_id));
EXPECT_EQ(NO_ERROR,
decryptor_->RemoveOfflineLicense(
original_key_set_id, security_level, kDefaultCdmIdentifier));
// Verify license has been removed.
EXPECT_EQ(NO_ERROR, decryptor_->ListStoredLicenses(
security_level, kDefaultCdmIdentifier, &key_set_ids));
EXPECT_THAT(key_set_ids, Not(Contains(original_key_set_id)));
// Re-provision.
Unprovision();
handle.DeleteAllFiles();
Provision();
// Part 2: Test when the entry does not match the license's key set ID.
EXPECT_TRUE(handle.StoreLicense(license_data, &sub_result));
EXPECT_EQ(DeviceFiles::kNoError, sub_result);
// Request another license so that the usage table is not empty.
decryptor_->OpenSession(config_.key_system(), nullptr, kDefaultCdmIdentifier,
nullptr, &session_id_);
GenerateKeyRequest(key_id, kLicenseTypeOffline);
VerifyKeyRequestResponse(config_.license_server(), client_auth);
decryptor_->CloseSession(session_id_);
// Get list of existing offline licenses.
EXPECT_EQ(NO_ERROR, decryptor_->ListStoredLicenses(
security_level, kDefaultCdmIdentifier, &key_set_ids));
EXPECT_THAT(key_set_ids, Contains(original_key_set_id));
EXPECT_EQ(NO_ERROR,
decryptor_->RemoveOfflineLicense(
original_key_set_id, security_level, kDefaultCdmIdentifier));
// Verify license has been removed.
EXPECT_EQ(NO_ERROR, decryptor_->ListStoredLicenses(
security_level, kDefaultCdmIdentifier, &key_set_ids));
EXPECT_THAT(key_set_ids, Not(Contains(original_key_set_id)));
}
class WvCdmRequestLicenseRollbackTest
: public WvCdmRequestLicenseTest,
public ::testing::WithParamInterface<SubSampleInfo*> {

View File

@@ -292,10 +292,11 @@ enum {
kLoadUsageEntryInvalidSession = ERROR_DRM_VENDOR_MIN + 309,
kMoveUsageEntryDestinationInUse = ERROR_DRM_VENDOR_MIN + 310,
kShrinkUsageTableHeaderEntryInUse = ERROR_DRM_VENDOR_MIN + 311,
kLicenseUsageEntryMissing = ERROR_DRM_VENDOR_MIN + 312,
// This should always follow the last error code.
// The offset value should be updated each time a new error code is added.
kErrorWVDrmMaxErrorUsed = ERROR_DRM_VENDOR_MIN + 311,
kErrorWVDrmMaxErrorUsed = ERROR_DRM_VENDOR_MIN + 312,
// Used by crypto test mode
kErrorTestMode = ERROR_DRM_VENDOR_MAX,

View File

@@ -356,6 +356,8 @@ static android::status_t mapCdmResponseType(wvcdm::CdmResponseType res) {
return kLicenseResponseParseError4;
case wvcdm::LICENSE_RESPONSE_PARSE_ERROR_5:
return kLicenseResponseParseError5;
case wvcdm::LICENSE_USAGE_ENTRY_MISSING:
return kLicenseUsageEntryMissing;
case wvcdm::LIST_LICENSE_ERROR_1:
return kListLicenseError1;
case wvcdm::LIST_LICENSE_ERROR_2:

View File

@@ -358,6 +358,7 @@ static Status mapCdmResponseType(wvcdm::CdmResponseType res) {
case wvcdm::LOAD_USAGE_ENTRY_INVALID_SESSION:
case wvcdm::MOVE_USAGE_ENTRY_DESTINATION_IN_USE:
case wvcdm::SHRINK_USAGE_TABLE_HEADER_ENTRY_IN_USE:
case wvcdm::LICENSE_USAGE_ENTRY_MISSING:
ALOGW("Returns UNKNOWN error for legacy status: %d", res);
return Status::ERROR_DRM_UNKNOWN;