Merge "Suppress error for removing lingering offline licenses." into rvc-dev am: 792e17a8c4 am: d7f5a74ea3
Change-Id: Idb87851939277a1950b0ad5605c48a772ae56a6d
This commit is contained in:
@@ -233,6 +233,12 @@ class CdmSession {
|
|||||||
virtual CdmResponseType AddKeyInternal(const CdmKeyResponse& key_response);
|
virtual CdmResponseType AddKeyInternal(const CdmKeyResponse& key_response);
|
||||||
void UpdateRequestLatencyTiming(CdmResponseType sts);
|
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.
|
// These setters are for testing only. Takes ownership of the pointers.
|
||||||
void set_license_parser(CdmLicense* license_parser);
|
void set_license_parser(CdmLicense* license_parser);
|
||||||
void set_crypto_session(CryptoSession* crypto_session);
|
void set_crypto_session(CryptoSession* crypto_session);
|
||||||
|
|||||||
@@ -411,6 +411,7 @@ enum CdmResponseType {
|
|||||||
LOAD_USAGE_ENTRY_INVALID_SESSION = 357,
|
LOAD_USAGE_ENTRY_INVALID_SESSION = 357,
|
||||||
MOVE_USAGE_ENTRY_DESTINATION_IN_USE = 358,
|
MOVE_USAGE_ENTRY_DESTINATION_IN_USE = 358,
|
||||||
SHRINK_USAGE_TABLE_HEADER_ENTRY_IN_USE = 359,
|
SHRINK_USAGE_TABLE_HEADER_ENTRY_IN_USE = 359,
|
||||||
|
LICENSE_USAGE_ENTRY_MISSING = 360,
|
||||||
// Don't forget to add new values to
|
// Don't forget to add new values to
|
||||||
// * core/test/test_printers.cpp.
|
// * core/test/test_printers.cpp.
|
||||||
// * android/include/mapErrors-inl.h
|
// * android/include/mapErrors-inl.h
|
||||||
|
|||||||
@@ -169,10 +169,13 @@ CdmResponseType CdmEngine::OpenKeySetSession(
|
|||||||
key_set_in_use =
|
key_set_in_use =
|
||||||
release_key_sets_.find(key_set_id) != release_key_sets_.end();
|
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;
|
CdmSessionId session_id;
|
||||||
CdmResponseType sts =
|
const CdmResponseType sts =
|
||||||
OpenSession(KEY_SYSTEM, property_set, event_listener,
|
OpenSession(KEY_SYSTEM, property_set, event_listener,
|
||||||
nullptr /* forced_session_id */, &session_id);
|
nullptr /* forced_session_id */, &session_id);
|
||||||
|
|
||||||
@@ -1114,14 +1117,19 @@ CdmResponseType CdmEngine::RemoveOfflineLicense(
|
|||||||
property_set.set_security_level(
|
property_set.set_security_level(
|
||||||
security_level == kSecurityLevelL3 ? kLevel3 : kLevelDefault);
|
security_level == kSecurityLevelL3 ? kLevel3 : kLevelDefault);
|
||||||
DeviceFiles handle(file_system_);
|
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,
|
CdmResponseType sts = OpenKeySetSession(key_set_id, &property_set,
|
||||||
nullptr /* event listener */);
|
nullptr /* event listener */);
|
||||||
if (sts != NO_ERROR) {
|
if (sts != NO_ERROR) {
|
||||||
if (!handle.Init(security_level)) {
|
LOGE("Failed to open key set session: status = %d", static_cast<int>(sts));
|
||||||
LOGE("Cannot initialize device files");
|
|
||||||
}
|
|
||||||
handle.DeleteLicense(key_set_id);
|
handle.DeleteLicense(key_set_id);
|
||||||
return REMOVE_OFFLINE_LICENSE_ERROR_1;
|
return sts;
|
||||||
}
|
}
|
||||||
|
|
||||||
CdmSessionId session_id;
|
CdmSessionId session_id;
|
||||||
@@ -1141,6 +1149,14 @@ CdmResponseType CdmEngine::RemoveOfflineLicense(
|
|||||||
session_id = iter->second.first;
|
session_id = iter->second.first;
|
||||||
sts = RemoveLicense(session_id);
|
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) {
|
if (sts != NO_ERROR) {
|
||||||
|
|||||||
@@ -282,6 +282,9 @@ CdmResponseType CdmSession::RestoreOfflineSession(const CdmKeySetId& key_set_id,
|
|||||||
key_response_, &provider_session_token) ||
|
key_response_, &provider_session_token) ||
|
||||||
usage_table_header_ == nullptr) {
|
usage_table_header_ == nullptr) {
|
||||||
provider_session_token.clear();
|
provider_session_token.clear();
|
||||||
|
} else if (!VerifyOfflineUsageEntry()) {
|
||||||
|
LOGE("License usage entry is invalid, cannot restore");
|
||||||
|
return LICENSE_USAGE_ENTRY_MISSING;
|
||||||
} else {
|
} else {
|
||||||
CdmResponseType sts = usage_table_header_->LoadEntry(
|
CdmResponseType sts = usage_table_header_->LoadEntry(
|
||||||
crypto_session_.get(), usage_entry_, usage_entry_number_);
|
crypto_session_.get(), usage_entry_, usage_entry_number_);
|
||||||
@@ -1131,6 +1134,25 @@ void CdmSession::UpdateRequestLatencyTiming(CdmResponseType sts) {
|
|||||||
license_request_latency_.Clear();
|
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
|
// For testing only - takes ownership of pointers
|
||||||
|
|
||||||
void CdmSession::set_license_parser(CdmLicense* license_parser) {
|
void CdmSession::set_license_parser(CdmLicense* license_parser) {
|
||||||
|
|||||||
@@ -521,6 +521,9 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
|
|||||||
case LICENSE_RESPONSE_PARSE_ERROR_5:
|
case LICENSE_RESPONSE_PARSE_ERROR_5:
|
||||||
*os << "LICENSE_RESPONSE_PARSE_ERROR_5";
|
*os << "LICENSE_RESPONSE_PARSE_ERROR_5";
|
||||||
break;
|
break;
|
||||||
|
case LICENSE_USAGE_ENTRY_MISSING:
|
||||||
|
*os << "LICENSE_USAGE_ENTRY_MISSING";
|
||||||
|
break;
|
||||||
case LIST_LICENSE_ERROR_1:
|
case LIST_LICENSE_ERROR_1:
|
||||||
*os << "LIST_LICENSE_ERROR_1";
|
*os << "LIST_LICENSE_ERROR_1";
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -5997,6 +5997,107 @@ TEST_F(WvCdmRequestLicenseTest, DISABLED_DecryptPathTest) {
|
|||||||
decryptor_->CloseSession(session_id_);
|
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
|
class WvCdmRequestLicenseRollbackTest
|
||||||
: public WvCdmRequestLicenseTest,
|
: public WvCdmRequestLicenseTest,
|
||||||
public ::testing::WithParamInterface<SubSampleInfo*> {
|
public ::testing::WithParamInterface<SubSampleInfo*> {
|
||||||
|
|||||||
@@ -292,10 +292,11 @@ enum {
|
|||||||
kLoadUsageEntryInvalidSession = ERROR_DRM_VENDOR_MIN + 309,
|
kLoadUsageEntryInvalidSession = ERROR_DRM_VENDOR_MIN + 309,
|
||||||
kMoveUsageEntryDestinationInUse = ERROR_DRM_VENDOR_MIN + 310,
|
kMoveUsageEntryDestinationInUse = ERROR_DRM_VENDOR_MIN + 310,
|
||||||
kShrinkUsageTableHeaderEntryInUse = ERROR_DRM_VENDOR_MIN + 311,
|
kShrinkUsageTableHeaderEntryInUse = ERROR_DRM_VENDOR_MIN + 311,
|
||||||
|
kLicenseUsageEntryMissing = ERROR_DRM_VENDOR_MIN + 312,
|
||||||
|
|
||||||
// This should always follow the last error code.
|
// This should always follow the last error code.
|
||||||
// The offset value should be updated each time a new error code is added.
|
// 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
|
// Used by crypto test mode
|
||||||
kErrorTestMode = ERROR_DRM_VENDOR_MAX,
|
kErrorTestMode = ERROR_DRM_VENDOR_MAX,
|
||||||
|
|||||||
@@ -356,6 +356,8 @@ static android::status_t mapCdmResponseType(wvcdm::CdmResponseType res) {
|
|||||||
return kLicenseResponseParseError4;
|
return kLicenseResponseParseError4;
|
||||||
case wvcdm::LICENSE_RESPONSE_PARSE_ERROR_5:
|
case wvcdm::LICENSE_RESPONSE_PARSE_ERROR_5:
|
||||||
return kLicenseResponseParseError5;
|
return kLicenseResponseParseError5;
|
||||||
|
case wvcdm::LICENSE_USAGE_ENTRY_MISSING:
|
||||||
|
return kLicenseUsageEntryMissing;
|
||||||
case wvcdm::LIST_LICENSE_ERROR_1:
|
case wvcdm::LIST_LICENSE_ERROR_1:
|
||||||
return kListLicenseError1;
|
return kListLicenseError1;
|
||||||
case wvcdm::LIST_LICENSE_ERROR_2:
|
case wvcdm::LIST_LICENSE_ERROR_2:
|
||||||
|
|||||||
@@ -358,6 +358,7 @@ static Status mapCdmResponseType(wvcdm::CdmResponseType res) {
|
|||||||
case wvcdm::LOAD_USAGE_ENTRY_INVALID_SESSION:
|
case wvcdm::LOAD_USAGE_ENTRY_INVALID_SESSION:
|
||||||
case wvcdm::MOVE_USAGE_ENTRY_DESTINATION_IN_USE:
|
case wvcdm::MOVE_USAGE_ENTRY_DESTINATION_IN_USE:
|
||||||
case wvcdm::SHRINK_USAGE_TABLE_HEADER_ENTRY_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);
|
ALOGW("Returns UNKNOWN error for legacy status: %d", res);
|
||||||
return Status::ERROR_DRM_UNKNOWN;
|
return Status::ERROR_DRM_UNKNOWN;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user