Merge changes Ie4a63ac2,I8523ef28 into pi-dev
* changes: Recovery from usage info corruption Correct segfault in WvCdmRequestLicenseTest.UsageRemoveSecureStopTest
This commit is contained in:
@@ -289,7 +289,7 @@ enum CdmResponseType {
|
||||
USAGE_STORE_LICENSE_FAILED = 247,
|
||||
USAGE_STORE_USAGE_INFO_FAILED = 248,
|
||||
USAGE_INVALID_LOAD_ENTRY = 249,
|
||||
REMOVE_ALL_USAGE_INFO_ERROR_4 = 250,
|
||||
/* previously REMOVE_ALL_USAGE_INFO_ERROR_4 = 250, */
|
||||
REMOVE_ALL_USAGE_INFO_ERROR_5 = 251,
|
||||
RELEASE_USAGE_INFO_FAILED = 252,
|
||||
INCORRECT_USAGE_SUPPORT_TYPE_1 = 253,
|
||||
|
||||
@@ -1223,24 +1223,29 @@ CdmResponseType CdmEngine::RemoveAllUsageInfo(const std::string& app_id) {
|
||||
if (!handle.RetrieveUsageInfo(
|
||||
DeviceFiles::GetUsageInfoFileName(app_id),
|
||||
&usage_data)) {
|
||||
status = REMOVE_ALL_USAGE_INFO_ERROR_4;
|
||||
LOGW("CdmEngine::RemoveAllUsageInfo: failed to retrieve usage info");
|
||||
break;
|
||||
}
|
||||
|
||||
if (usage_data.empty()) break;
|
||||
|
||||
status = usage_session_->DeleteUsageEntry(
|
||||
CdmResponseType res = usage_session_->DeleteUsageEntry(
|
||||
usage_data[0].usage_entry_number);
|
||||
|
||||
if (status != NO_ERROR) break;
|
||||
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)) {
|
||||
status = REMOVE_ALL_USAGE_INFO_ERROR_6;
|
||||
LOGW("CdmEngine::RemoveAllUsageInfo: failed to delete usage "
|
||||
"info");
|
||||
break;
|
||||
}
|
||||
} while (status == NO_ERROR && !usage_data.empty());
|
||||
} while (!usage_data.empty());
|
||||
|
||||
std::vector<std::string> provider_session_tokens;
|
||||
if (!handle.DeleteAllUsageInfoForApp(
|
||||
@@ -1315,8 +1320,7 @@ CdmResponseType CdmEngine::RemoveUsageInfo(
|
||||
|
||||
switch (usage_session_->get_usage_support_type()) {
|
||||
case kUsageEntrySupport: {
|
||||
status = usage_session_->DeleteUsageEntry(
|
||||
usage_data[0].usage_entry_number);
|
||||
status = usage_session_->DeleteUsageEntry(usage_entry_number);
|
||||
|
||||
if (!handle.DeleteUsageInfo(
|
||||
DeviceFiles::GetUsageInfoFileName(app_id),
|
||||
|
||||
@@ -841,6 +841,25 @@ CdmResponseType CdmSession::StoreLicense() {
|
||||
key_set_id_, usage_entry_,
|
||||
usage_entry_number_)) {
|
||||
LOGE("CdmSession::StoreLicense: Unable to store usage info");
|
||||
// Usage info file is corrupt. Delete current usage entry and file.
|
||||
switch (usage_support_type_) {
|
||||
case kUsageEntrySupport:
|
||||
DeleteUsageEntry(usage_entry_number_);
|
||||
break;
|
||||
case kUsageTableSupport:
|
||||
crypto_session_->DeleteUsageInformation(provider_session_token);
|
||||
crypto_session_->UpdateUsageInformation();
|
||||
break;
|
||||
default:
|
||||
LOGW("CdmSession::StoreLicense: unexpected usage support type: %d",
|
||||
usage_support_type_);
|
||||
break;
|
||||
}
|
||||
std::vector<std::string> provider_session_tokens;
|
||||
file_handle_->DeleteAllUsageInfoForApp(
|
||||
DeviceFiles::GetUsageInfoFileName(app_id),
|
||||
&provider_session_tokens);
|
||||
|
||||
return STORE_USAGE_INFO_ERROR;
|
||||
}
|
||||
return NO_ERROR;
|
||||
|
||||
@@ -227,9 +227,6 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
|
||||
case REMOVE_ALL_USAGE_INFO_ERROR_2:
|
||||
*os << "REMOVE_ALL_USAGE_INFO_ERROR_2";
|
||||
break;
|
||||
case REMOVE_ALL_USAGE_INFO_ERROR_4:
|
||||
*os << "REMOVE_ALL_USAGE_INFO_ERROR_4";
|
||||
break;
|
||||
case REMOVE_ALL_USAGE_INFO_ERROR_5:
|
||||
*os << "REMOVE_ALL_USAGE_INFO_ERROR_5";
|
||||
break;
|
||||
|
||||
@@ -1475,13 +1475,11 @@ class WvCdmRequestLicenseTest : public WvCdmTestBase {
|
||||
|
||||
void VerifyKeyRequestResponse(const std::string& server_url,
|
||||
const std::string& client_auth) {
|
||||
std::string response;
|
||||
VerifyKeyRequestResponse(server_url, client_auth, false);
|
||||
}
|
||||
|
||||
void VerifyUsageKeyRequestResponse(const std::string& server_url,
|
||||
const std::string& client_auth) {
|
||||
std::string response;
|
||||
VerifyKeyRequestResponse(server_url, client_auth, true);
|
||||
}
|
||||
|
||||
@@ -1496,10 +1494,19 @@ class WvCdmRequestLicenseTest : public WvCdmTestBase {
|
||||
const std::string& client_auth,
|
||||
bool is_usage,
|
||||
std::string* response) {
|
||||
VerifyKeyRequestResponse(wvcdm::KEY_ADDED, server_url, client_auth,
|
||||
is_usage, response);
|
||||
}
|
||||
|
||||
void VerifyKeyRequestResponse(CdmResponseType expected_response,
|
||||
const std::string& server_url,
|
||||
const std::string& client_auth,
|
||||
bool is_usage,
|
||||
std::string* response) {
|
||||
*response = GetKeyRequestResponse(server_url, client_auth);
|
||||
|
||||
EXPECT_EQ(decryptor_.AddKey(session_id_, *response, &key_set_id_),
|
||||
wvcdm::KEY_ADDED);
|
||||
expected_response);
|
||||
EXPECT_EQ(is_usage || license_type_ == kLicenseTypeOffline,
|
||||
key_set_id_.size() > 0);
|
||||
}
|
||||
@@ -3116,6 +3123,156 @@ TEST_F(WvCdmRequestLicenseTest, UsageRemoveAllTest) {
|
||||
EXPECT_TRUE(usage_info.empty());
|
||||
}
|
||||
|
||||
TEST_F(WvCdmRequestLicenseTest, RemoveCorruptedUsageInfoTest) {
|
||||
Unprovision();
|
||||
|
||||
std::string app_id_empty = "";
|
||||
std::string app_id_not_empty = "not empty";
|
||||
|
||||
TestWvCdmClientPropertySet property_set;
|
||||
Provision(kLevelDefault);
|
||||
|
||||
CdmSecurityLevel security_level = GetDefaultSecurityLevel();
|
||||
FileSystem file_system;
|
||||
DeviceFiles handle(&file_system);
|
||||
EXPECT_TRUE(handle.Init(security_level));
|
||||
std::vector<std::string> psts;
|
||||
EXPECT_TRUE(handle.DeleteAllUsageInfoForApp(
|
||||
DeviceFiles::GetUsageInfoFileName(""), &psts));
|
||||
|
||||
for (size_t i = 0; i < N_ELEM(usage_info_sub_samples_icp); ++i) {
|
||||
SubSampleInfo* data = usage_info_sub_samples_icp + i;
|
||||
property_set.set_app_id(i % 2 == 0 ? app_id_empty : app_id_not_empty);
|
||||
decryptor_.OpenSession(g_key_system, &property_set, kDefaultCdmIdentifier,
|
||||
NULL, &session_id_);
|
||||
std::string key_id = a2bs_hex(
|
||||
"000000427073736800000000" // blob size and pssh
|
||||
"EDEF8BA979D64ACEA3C827DCD51D21ED00000022" // Widevine system id
|
||||
"08011a0d7769646576696e655f74657374220f73" // pssh data
|
||||
"747265616d696e675f636c6970");
|
||||
|
||||
char ch = 0x33 + i;
|
||||
key_id.append(1, ch);
|
||||
|
||||
GenerateKeyRequest(key_id, kLicenseTypeStreaming, &property_set);
|
||||
|
||||
// TODO(rfrias): streaming_clip6 is a streaming license without a pst
|
||||
if (ch == '6')
|
||||
VerifyKeyRequestResponse(g_license_server, g_client_auth, false);
|
||||
else
|
||||
VerifyUsageKeyRequestResponse(g_license_server, g_client_auth);
|
||||
|
||||
std::vector<uint8_t> decrypt_buffer(data->encrypt_data.size());
|
||||
CdmDecryptionParameters decryption_parameters(
|
||||
&data->key_id, &data->encrypt_data.front(), data->encrypt_data.size(),
|
||||
&data->iv, data->block_offset, &decrypt_buffer[0]);
|
||||
decryption_parameters.is_encrypted = data->is_encrypted;
|
||||
decryption_parameters.is_secure = data->is_secure;
|
||||
decryption_parameters.subsample_flags = data->subsample_flags;
|
||||
EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_, data->validate_key_id,
|
||||
decryption_parameters));
|
||||
|
||||
EXPECT_TRUE(std::equal(data->decrypt_data.begin(), data->decrypt_data.end(),
|
||||
decrypt_buffer.begin()));
|
||||
decryptor_.CloseSession(session_id_);
|
||||
}
|
||||
|
||||
CdmUsageInfo usage_info;
|
||||
EXPECT_EQ(
|
||||
KEY_MESSAGE,
|
||||
decryptor_.GetUsageInfo(app_id_empty, kDefaultCdmIdentifier,
|
||||
&usage_info));
|
||||
EXPECT_TRUE(usage_info.size() > 0);
|
||||
EXPECT_EQ(
|
||||
KEY_MESSAGE,
|
||||
decryptor_.GetUsageInfo(app_id_not_empty, kDefaultCdmIdentifier,
|
||||
&usage_info));
|
||||
EXPECT_TRUE(usage_info.size() > 0);
|
||||
|
||||
// Read in usage info file
|
||||
std::string path;
|
||||
EXPECT_TRUE(Properties::GetDeviceFilesBasePath(security_level, &path));
|
||||
std::string usage_info_not_empty_app_id_file_name =
|
||||
path + DeviceFiles::GetUsageInfoFileName(app_id_not_empty);
|
||||
|
||||
ssize_t file_size =
|
||||
file_system.FileSize(usage_info_not_empty_app_id_file_name);
|
||||
EXPECT_LT(4, file_size);
|
||||
File* file =
|
||||
file_system.Open(usage_info_not_empty_app_id_file_name,
|
||||
FileSystem::kReadOnly);
|
||||
EXPECT_TRUE(NULL != file);
|
||||
std::string file_data;
|
||||
file_data.resize(file_size);
|
||||
ssize_t bytes = file->Read(&file_data[0], file_data.size());
|
||||
EXPECT_EQ(file_size, bytes);
|
||||
file->Close();
|
||||
|
||||
// Corrupt the hash of the usage info file with non-empty app id and write
|
||||
// it back out
|
||||
memset(&file_data[0]+bytes-4, 0, 4);
|
||||
file = file_system.Open(usage_info_not_empty_app_id_file_name,
|
||||
FileSystem::kCreate | FileSystem::kTruncate);
|
||||
EXPECT_TRUE(NULL != file);
|
||||
bytes = file->Write(file_data.data(), file_data.size());
|
||||
EXPECT_EQ(file_size, bytes);
|
||||
file->Close();
|
||||
|
||||
EXPECT_EQ(
|
||||
NO_ERROR,
|
||||
decryptor_.RemoveAllUsageInfo(app_id_not_empty, kDefaultCdmIdentifier));
|
||||
|
||||
EXPECT_EQ(
|
||||
NO_ERROR,
|
||||
decryptor_.GetUsageInfo(app_id_not_empty, kDefaultCdmIdentifier,
|
||||
&usage_info));
|
||||
EXPECT_TRUE(usage_info.empty());
|
||||
EXPECT_EQ(
|
||||
KEY_MESSAGE,
|
||||
decryptor_.GetUsageInfo(app_id_empty, kDefaultCdmIdentifier,
|
||||
&usage_info));
|
||||
EXPECT_TRUE(usage_info.size() > 0);
|
||||
|
||||
// Read in usage info file
|
||||
std::string usage_info_empty_app_id_file_name =
|
||||
path + DeviceFiles::GetUsageInfoFileName(app_id_empty);
|
||||
|
||||
file_size = file_system.FileSize(usage_info_empty_app_id_file_name);
|
||||
EXPECT_LT(4, file_size);
|
||||
file = file_system.Open(usage_info_empty_app_id_file_name,
|
||||
FileSystem::kReadOnly);
|
||||
EXPECT_TRUE(NULL != file);
|
||||
file_data.resize(file_size);
|
||||
bytes = file->Read(&file_data[0], file_data.size());
|
||||
EXPECT_EQ(file_size, bytes);
|
||||
file->Close();
|
||||
|
||||
// Corrupt the hash of the usage info file with empty app id and write it
|
||||
// back out
|
||||
memset(&file_data[0]+bytes-4, 0, 4);
|
||||
file = file_system.Open(usage_info_empty_app_id_file_name,
|
||||
FileSystem::kCreate | FileSystem::kTruncate);
|
||||
EXPECT_TRUE(NULL != file);
|
||||
bytes = file->Write(file_data.data(), file_data.size());
|
||||
EXPECT_EQ(file_size, bytes);
|
||||
file->Close();
|
||||
|
||||
EXPECT_EQ(
|
||||
NO_ERROR,
|
||||
decryptor_.RemoveAllUsageInfo(app_id_empty, kDefaultCdmIdentifier));
|
||||
|
||||
EXPECT_EQ(
|
||||
NO_ERROR,
|
||||
decryptor_.GetUsageInfo(app_id_not_empty, kDefaultCdmIdentifier,
|
||||
&usage_info));
|
||||
EXPECT_TRUE(usage_info.empty());
|
||||
EXPECT_EQ(
|
||||
NO_ERROR,
|
||||
decryptor_.GetUsageInfo(app_id_empty, kDefaultCdmIdentifier,
|
||||
&usage_info));
|
||||
EXPECT_TRUE(usage_info.empty());
|
||||
}
|
||||
|
||||
TEST_F(WvCdmRequestLicenseTest, GetSecureStopIdsTest) {
|
||||
Unprovision();
|
||||
|
||||
@@ -3276,6 +3433,115 @@ TEST_F(WvCdmRequestLicenseTest, GetSecureStopIdsTest) {
|
||||
EXPECT_TRUE(retrieved_secure_stop_ids.empty());
|
||||
}
|
||||
|
||||
TEST_F(WvCdmRequestLicenseTest, UsageRecoveryTest) {
|
||||
Unprovision();
|
||||
|
||||
std::string app_id_empty = "";
|
||||
|
||||
TestWvCdmClientPropertySet property_set;
|
||||
Provision(kLevelDefault);
|
||||
|
||||
CdmSecurityLevel security_level = GetDefaultSecurityLevel();
|
||||
FileSystem file_system;
|
||||
DeviceFiles handle(&file_system);
|
||||
EXPECT_TRUE(handle.Init(security_level));
|
||||
std::vector<std::string> psts;
|
||||
EXPECT_TRUE(handle.DeleteAllUsageInfoForApp(
|
||||
DeviceFiles::GetUsageInfoFileName(""), &psts));
|
||||
|
||||
// Fetch a usage license
|
||||
SubSampleInfo* data = kUsageLicenseTestVector1[0].sub_sample;
|
||||
|
||||
property_set.set_app_id(app_id_empty);
|
||||
decryptor_.OpenSession(g_key_system, &property_set, kDefaultCdmIdentifier,
|
||||
NULL, &session_id_);
|
||||
GenerateKeyRequest(kUsageLicenseTestVector1[0].pssh, kLicenseTypeStreaming,
|
||||
&property_set);
|
||||
|
||||
VerifyUsageKeyRequestResponse(g_license_server, g_client_auth);
|
||||
|
||||
std::vector<uint8_t> decrypt_buffer(data->encrypt_data.size());
|
||||
CdmDecryptionParameters decryption_parameters(
|
||||
&data->key_id, &data->encrypt_data.front(), data->encrypt_data.size(),
|
||||
&data->iv, data->block_offset, &decrypt_buffer[0]);
|
||||
decryption_parameters.is_encrypted = data->is_encrypted;
|
||||
decryption_parameters.is_secure = data->is_secure;
|
||||
decryption_parameters.subsample_flags = data->subsample_flags;
|
||||
EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_, data->validate_key_id,
|
||||
decryption_parameters));
|
||||
|
||||
EXPECT_TRUE(std::equal(data->decrypt_data.begin(), data->decrypt_data.end(),
|
||||
decrypt_buffer.begin()));
|
||||
decryptor_.CloseSession(session_id_);
|
||||
|
||||
std::string path;
|
||||
EXPECT_TRUE(Properties::GetDeviceFilesBasePath(security_level, &path));
|
||||
std::string usage_info_file_name =
|
||||
path + DeviceFiles::GetUsageInfoFileName(app_id_empty);
|
||||
|
||||
// Read in usage info file
|
||||
ssize_t file_size = file_system.FileSize(usage_info_file_name);
|
||||
EXPECT_LT(4, file_size);
|
||||
File* file =
|
||||
file_system.Open(usage_info_file_name, FileSystem::kReadOnly);
|
||||
EXPECT_TRUE(NULL != file);
|
||||
std::string file_data;
|
||||
file_data.resize(file_size);
|
||||
ssize_t bytes = file->Read(&file_data[0], file_data.size());
|
||||
EXPECT_EQ(file_size, bytes);
|
||||
file->Close();
|
||||
|
||||
// Corrupt the hash of the usage info file and write it back out
|
||||
memset(&file_data[0]+bytes-4, 0, 4);
|
||||
file = file_system.Open(usage_info_file_name,
|
||||
FileSystem::kCreate | FileSystem::kTruncate);
|
||||
EXPECT_TRUE(NULL != file);
|
||||
bytes = file->Write(file_data.data(), file_data.size());
|
||||
EXPECT_EQ(file_size, bytes);
|
||||
file->Close();
|
||||
|
||||
// Fetch a second usage license, this should fail as the usage table is
|
||||
// corrupt
|
||||
decryptor_.OpenSession(g_key_system, &property_set, kDefaultCdmIdentifier,
|
||||
NULL, &session_id_);
|
||||
GenerateKeyRequest(kUsageLicenseTestVector1[1].pssh, kLicenseTypeStreaming,
|
||||
&property_set);
|
||||
|
||||
std::string response;
|
||||
VerifyKeyRequestResponse(wvcdm::STORE_USAGE_INFO_ERROR, g_license_server,
|
||||
g_client_auth, true, &response);
|
||||
|
||||
decryptor_.CloseSession(session_id_);
|
||||
|
||||
// Fetch the second usage license and verify that it is usable
|
||||
decryptor_.OpenSession(g_key_system, &property_set, kDefaultCdmIdentifier,
|
||||
NULL, &session_id_);
|
||||
GenerateKeyRequest(kUsageLicenseTestVector1[1].pssh, kLicenseTypeStreaming,
|
||||
&property_set);
|
||||
|
||||
VerifyUsageKeyRequestResponse(g_license_server, g_client_auth);
|
||||
|
||||
data = kUsageLicenseTestVector1[1].sub_sample;
|
||||
|
||||
decrypt_buffer.resize(data->encrypt_data.size());
|
||||
decryption_parameters.key_id = &data->key_id;
|
||||
decryption_parameters.encrypt_buffer = &data->encrypt_data.front();
|
||||
decryption_parameters.encrypt_length = data->encrypt_data.size();
|
||||
decryption_parameters.iv = &data->iv;
|
||||
decryption_parameters.block_offset = data->block_offset;
|
||||
decryption_parameters.decrypt_buffer = &decrypt_buffer[0];
|
||||
decryption_parameters.decrypt_buffer_length = data->encrypt_data.size();
|
||||
decryption_parameters.is_encrypted = data->is_encrypted;
|
||||
decryption_parameters.is_secure = data->is_secure;
|
||||
decryption_parameters.subsample_flags = data->subsample_flags;
|
||||
EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_, data->validate_key_id,
|
||||
decryption_parameters));
|
||||
|
||||
EXPECT_TRUE(std::equal(data->decrypt_data.begin(), data->decrypt_data.end(),
|
||||
decrypt_buffer.begin()));
|
||||
decryptor_.CloseSession(session_id_);
|
||||
}
|
||||
|
||||
TEST_F(WvCdmRequestLicenseTest, UsageRemoveSecureStopTest) {
|
||||
Unprovision();
|
||||
|
||||
|
||||
@@ -231,7 +231,6 @@ enum {
|
||||
kUsageStoreLicenseFailed = ERROR_DRM_VENDOR_MIN + 241,
|
||||
kUsageStoreUsageInfoFailed = ERROR_DRM_VENDOR_MIN + 242,
|
||||
kUsageInvalidLoadEntry = ERROR_DRM_VENDOR_MIN + 243,
|
||||
kRemoveAllUsageInfoError4 = ERROR_DRM_VENDOR_MIN + 244,
|
||||
kRemoveAllUsageInfoError5 = ERROR_DRM_VENDOR_MIN + 245,
|
||||
kReleaseUsageInfoFailed = ERROR_DRM_VENDOR_MIN + 246,
|
||||
kIncorrectUsageSupportType1 = ERROR_DRM_VENDOR_MIN + 247,
|
||||
|
||||
@@ -447,8 +447,6 @@ static android::status_t mapCdmResponseType(wvcdm::CdmResponseType res) {
|
||||
return kIncorrectUsageSupportType1;
|
||||
case wvcdm::INCORRECT_USAGE_SUPPORT_TYPE_2:
|
||||
return kIncorrectUsageSupportType2;
|
||||
case wvcdm::REMOVE_ALL_USAGE_INFO_ERROR_4:
|
||||
return kRemoveAllUsageInfoError4;
|
||||
case wvcdm::REMOVE_ALL_USAGE_INFO_ERROR_5:
|
||||
return kRemoveAllUsageInfoError5;
|
||||
case wvcdm::NO_USAGE_ENTRIES:
|
||||
|
||||
@@ -264,7 +264,6 @@ static Status mapCdmResponseType(wvcdm::CdmResponseType res) {
|
||||
case wvcdm::USAGE_STORE_LICENSE_FAILED:
|
||||
case wvcdm::USAGE_STORE_USAGE_INFO_FAILED:
|
||||
case wvcdm::USAGE_INVALID_LOAD_ENTRY:
|
||||
case wvcdm::REMOVE_ALL_USAGE_INFO_ERROR_4:
|
||||
case wvcdm::REMOVE_ALL_USAGE_INFO_ERROR_5:
|
||||
case wvcdm::REMOVE_ALL_USAGE_INFO_ERROR_6:
|
||||
case wvcdm::REMOVE_ALL_USAGE_INFO_ERROR_7:
|
||||
|
||||
Reference in New Issue
Block a user