Check if license exists before calling remove.

[ Partial cherry-pick of http://go/wvgerrit/186230 ]

The removeOfflineLicense() API in the Media DRM plug would attempt
to remove the specified license from L1, then retry L3 if L1 failed
for any reason.  This causes error emitted by L1 to be masked by
errors emitted from L3.  In particular, if an internal error occurs
on L1 when removing the license, because the plugin would then
try L3 which does not contain the license, the app will receive either
a "does not exist" or "needs provisioning" error from L3.

This CL changes the plugin to first determines which security level
the license exists for.  Then only attempts removal on that security
level.

Bug: 301910628
Bug: 291181955
Bug: 296300842
Bug: 302612540
Test: MediaDrmParameterizedTests GTS on bluejay
Merged from https://widevine-internal-review.googlesource.com/187611

Merged from https://widevine-internal-review.googlesource.com/187832

Change-Id: I3d3975f945d2e97cfa9d866baf6ca5cf901f8af5
This commit is contained in:
Alex Dale
2023-11-09 17:30:27 -08:00
committed by Robert Shih
parent 1183ae813f
commit 57d231db1b
2 changed files with 97 additions and 19 deletions

View File

@@ -1061,29 +1061,42 @@ Status WVDrmPlugin::unprovisionDevice() {
::ndk::ScopedAStatus WVDrmPlugin::removeOfflineLicense(
const ::aidl::android::hardware::drm::KeySetId& in_keySetId) {
if (!in_keySetId.keySetId.size()) {
if (in_keySetId.keySetId.empty()) {
return toNdkScopedAStatus(Status::BAD_VALUE);
}
CdmIdentifier identifier;
auto status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
const auto status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
if (status != Status::OK) {
return toNdkScopedAStatus(status);
}
CdmResponseType res(wvcdm::UNKNOWN_ERROR);
const std::vector<CdmSecurityLevel> levels = {wvcdm::kSecurityLevelL1,
wvcdm::kSecurityLevelL3};
const CdmKeySetId cdmKeySetId(in_keySetId.keySetId.begin(),
in_keySetId.keySetId.end());
res = mCDM->RemoveOfflineLicense(
std::string(in_keySetId.keySetId.begin(), in_keySetId.keySetId.end()),
wvcdm::kSecurityLevelL1, identifier);
if (!isCdmResponseTypeSuccess(res)) {
CdmResponseType res = mCDM->RemoveOfflineLicense(
std::string(in_keySetId.keySetId.begin(), in_keySetId.keySetId.end()),
wvcdm::kSecurityLevelL3, identifier);
return toNdkScopedAStatus(mapCdmResponseType(res));
for (const CdmSecurityLevel level : levels) {
std::vector<CdmKeySetId> keySetIds;
const CdmResponseType res =
mCDM->ListStoredLicenses(level, identifier, &keySetIds);
if (!isCdmResponseTypeSuccess(res)) {
// This could failure for several reasons, but none that are
// worth returning to the app at this time.
ALOGW("Failed to list stored licenses: res = %d", static_cast<int>(res));
continue;
}
// Check if exists.
if (keySetIds.empty() || std::find(keySetIds.begin(), keySetIds.end(),
cdmKeySetId) == keySetIds.end()) {
// Does not exist for this security level.
continue;
}
return toNdkScopedAStatus(mapCdmResponseType(
mCDM->RemoveOfflineLicense(cdmKeySetId, level, identifier)));
}
return toNdkScopedAStatus(Status::OK);
// Could only reach this state if the key set could not be found.
return toNdkScopedAStatus(Status::BAD_VALUE);
}
::ndk::ScopedAStatus WVDrmPlugin::getPropertyString(

View File

@@ -2971,13 +2971,78 @@ TEST_F(WVDrmPluginHalTest, GetOfflineLicenseState) {
ASSERT_EQ(OfflineLicenseState::UNKNOWN, result);
}
TEST_F(WVDrmPluginHalTest, RemoveOfflineLicense) {
EXPECT_CALL(
*mCdm, RemoveOfflineLicense(_, kSecurityLevelL1, HasOrigin(EMPTY_ORIGIN)))
.Times(1);
TEST_F(WVDrmPluginHalTest, RemoveOfflineLicense_L1) {
// Key set to remove.
const CdmKeySetId cdmKeySetId = "ksidDEADBEAF";
const KeySetId keySetId{
std::vector<uint8_t>(cdmKeySetId.begin(), cdmKeySetId.end())};
// Desired key set ID found in L1.
const std::vector<CdmKeySetId> cdmKeySetIdsL1 = {"ksid1234", "ksid9876",
"ksid9999", cdmKeySetId,
"ksidBAD", "ksidCAFEB0BA"};
auto ret = mPlugin->removeOfflineLicense(keySetId);
EXPECT_TRUE(ret.isOk());
EXPECT_CALL(*mCdm, ListStoredLicenses(kSecurityLevelL1, _, NotNull()))
.WillOnce(DoAll(SetArgPointee<2>(cdmKeySetIdsL1),
testing::Return(CdmResponseType(wvcdm::NO_ERROR))));
// Only call L1.
EXPECT_CALL(*mCdm, RemoveOfflineLicense(cdmKeySetId, kSecurityLevelL1, _))
.WillOnce(testing::Return(CdmResponseType(wvcdm::NO_ERROR)));
EXPECT_CALL(*mCdm, RemoveOfflineLicense(_, kSecurityLevelL3, _)).Times(0);
const auto status = mPlugin->removeOfflineLicense(keySetId);
ASSERT_TRUE(status.isOk());
}
TEST_F(WVDrmPluginHalTest, RemoveOfflineLicense_L3) {
// Key set to remove.
const CdmKeySetId cdmKeySetId = "ksidDEADBEAF";
const KeySetId keySetId{
std::vector<uint8_t>(cdmKeySetId.begin(), cdmKeySetId.end())};
// Desired key set ID is not found in L1.
const std::vector<CdmKeySetId> cdmKeySetIdsL1 = {"ksid1234", "ksid9876",
"ksid9999"};
// Desired key set ID found in L3.
const std::vector<CdmKeySetId> cdmKeySetIdsL3 = {
"ksidDEADC0DE", "ksid1337", cdmKeySetId, "ksidBAD", "ksidCAFEB0BA"};
EXPECT_CALL(*mCdm, ListStoredLicenses(kSecurityLevelL1, _, NotNull()))
.WillOnce(DoAll(SetArgPointee<2>(cdmKeySetIdsL1),
testing::Return(CdmResponseType(wvcdm::NO_ERROR))));
EXPECT_CALL(*mCdm, ListStoredLicenses(kSecurityLevelL3, _, NotNull()))
.WillOnce(DoAll(SetArgPointee<2>(cdmKeySetIdsL3),
testing::Return(CdmResponseType(wvcdm::NO_ERROR))));
// Only call L3.
EXPECT_CALL(*mCdm, RemoveOfflineLicense(_, kSecurityLevelL1, _)).Times(0);
EXPECT_CALL(*mCdm, RemoveOfflineLicense(cdmKeySetId, kSecurityLevelL3, _))
.WillOnce(testing::Return(CdmResponseType(wvcdm::NO_ERROR)));
const auto status = mPlugin->removeOfflineLicense(keySetId);
ASSERT_TRUE(status.isOk());
}
TEST_F(WVDrmPluginHalTest, RemoveOfflineLicense_NotFound) {
// Key set to remove.
const CdmKeySetId cdmKeySetId = "ksidDEADBEAF";
const KeySetId keySetId{
std::vector<uint8_t>(cdmKeySetId.begin(), cdmKeySetId.end())};
// Desired key set ID is not found in L1.
const std::vector<CdmKeySetId> cdmKeySetIdsL1 = {"ksid1234", "ksid9876",
"ksid9999"};
// Desired key set ID is not found in L3.
const std::vector<CdmKeySetId> cdmKeySetIdsL3 = {"ksidDEADC0DE", "ksid1337",
"ksidBAD", "ksidCAFEB0BA"};
EXPECT_CALL(*mCdm, ListStoredLicenses(kSecurityLevelL1, _, NotNull()))
.WillOnce(DoAll(SetArgPointee<2>(cdmKeySetIdsL1),
testing::Return(CdmResponseType(wvcdm::NO_ERROR))));
EXPECT_CALL(*mCdm, ListStoredLicenses(kSecurityLevelL3, _, NotNull()))
.WillOnce(DoAll(SetArgPointee<2>(cdmKeySetIdsL3),
testing::Return(CdmResponseType(wvcdm::NO_ERROR))));
// No call to RemoveOfflineLicense should be made.
EXPECT_CALL(*mCdm, RemoveOfflineLicense(_, _, _)).Times(0);
const auto status = mPlugin->removeOfflineLicense(keySetId);
ASSERT_FALSE(status.isOk());
}
TEST_F(WVDrmPluginHalTest, CanStoreAtscLicense) {