Support ATSC license installation

[ Merge of http://go/wvgerrit/163900/ ]

ATSC licenses can be saved by calling
MediaDrm#setPropertyString("storeAtscLicense",<value>)
where <value> is
"<atsc-key-set-ID>:<license-file-data in Base64 format>"

Before storing an ATSC license a session must be opened and the
ATSC mode must be enabled.

Use MediaDrm#setPropertyString("atscMode","enable");

Bug: 176871821
Test: WV Unit/integration/Luci tests
Test: libwvdrmdrmplugin_hal_test
Test: GtsMediaTestCases
Change-Id: Iec2a8b7f87b1122395d06856202278b92316fdfe
This commit is contained in:
Rahul Frias
2022-12-17 19:34:33 -08:00
parent 233bac3a6f
commit 1e15b36b1a
9 changed files with 388 additions and 0 deletions

View File

@@ -239,6 +239,11 @@ public:
MOCK_METHOD(CdmResponseType, RemoveOfflineLicense,
(const std::string &, CdmSecurityLevel, const CdmIdentifier &),
(override));
MOCK_METHOD(CdmResponseType, StoreAtscLicense,
(const CdmIdentifier &, wvcdm::RequestedSecurityLevel,
const std::string &, const std::string &),
(override));
};
class MockCrypto : public WVGenericCryptoInterface {
@@ -2909,6 +2914,253 @@ TEST_F(WVDrmPluginHalTest, RemoveOfflineLicense) {
EXPECT_TRUE(ret.isOk());
}
TEST_F(WVDrmPluginHalTest, CanStoreAtscLicense) {
android::sp<StrictMock<MockCDM>> cdm = new StrictMock<MockCDM>();
StrictMock<MockCrypto> crypto;
std::string appPackageName = "com.test.package";
const CdmClientPropertySet *propertySet = nullptr;
CdmIdentifier mCdmIdAtscModeSet;
// Generate ATSC keyset and license data
uint8_t atscKeySetIdRaw[kKeySetIdSize];
std::vector<uint8_t> licenseDataRaw;
const size_t kLicenseDataSize = 512;
licenseDataRaw.resize(kLicenseDataSize);
FILE *fp = fopen("/dev/urandom", "r");
ASSERT_NE(fp, nullptr) << "Failed to open /dev/urandom";
fread(atscKeySetIdRaw, sizeof(uint8_t), sizeof(atscKeySetIdRaw), fp);
fread(&licenseDataRaw[0], sizeof(uint8_t), licenseDataRaw.size(), fp);
fclose(fp);
// Generate an ATSC key set ID of the form <ATSC prefix>+<random hex>
// with a total length of kKeySetIdSize
std::string atscKeySetId(wvcdm::ATSC_KEY_SET_ID_PREFIX);
atscKeySetId.append(wvutil::HexEncode(atscKeySetIdRaw, kKeySetIdSize));
atscKeySetId.resize(kKeySetIdSize);
memcpy(atscKeySetIdRaw, atscKeySetId.c_str(), sizeof(atscKeySetIdRaw));
std::string licenseDataBase64 = Base64Encode(licenseDataRaw);
std::string atscLicenseData = atscKeySetId + ":" + licenseDataBase64;
// Provide expected mock behavior
{
// Provide expected behavior in response to OpenSession and store the
// property set
EXPECT_CALL(*cdm, OpenSession(_, _, _, _, _))
.WillOnce(DoAll(SetArgPointee<4>(mCdmSessionId),
SaveArg<1>(&propertySet),
SaveArg<2>(&mCdmIdAtscModeSet),
testing::Return(CdmResponseType(wvcdm::NO_ERROR))));
// Provide expected behavior when plugin requests session control info
EXPECT_CALL(*cdm, QueryOemCryptoSessionId(mCdmSessionId, _))
.WillRepeatedly(Invoke(setSessionIdOnMap<4>));
EXPECT_CALL(
*cdm, StoreAtscLicense(HasOrigin(EMPTY_ORIGIN), wvcdm::kLevelDefault,
ElementsAreArray(atscKeySetIdRaw, kKeySetIdSize),
ElementsAreArray(licenseDataRaw)))
.Times(1);
EXPECT_CALL(*cdm, CloseSession(_)).Times(1);
}
std::shared_ptr<WVDrmPlugin> plugin = ::ndk::SharedRefBase::make<WVDrmPlugin>(
cdm.get(), appPackageName, &crypto, false);
// Test turning on ATSC mode
auto ret =
plugin->setPropertyString(std::string("atscMode"), std::string("enable"));
EXPECT_TRUE(ret.isOk());
ret = plugin->openSession(SecurityLevel::DEFAULT, &sessionId);
EXPECT_TRUE(ret.isOk());
ASSERT_THAT(propertySet, NotNull());
EXPECT_TRUE(propertySet->use_atsc_mode());
EXPECT_EQ(mCdmIdAtscModeSet.app_package_name, wvcdm::ATSC_APP_PACKAGE_NAME);
ret = plugin->setPropertyString(std::string("storeAtscLicense"),
atscLicenseData);
EXPECT_TRUE(ret.isOk());
ret = plugin->closeSession(sessionId);
EXPECT_TRUE(ret.isOk());
}
TEST_F(WVDrmPluginHalTest, FailsToStoreAtscLicense) {
android::sp<StrictMock<MockCDM>> cdm1 = new StrictMock<MockCDM>();
android::sp<StrictMock<MockCDM>> cdm2 = new StrictMock<MockCDM>();
android::sp<StrictMock<MockCDM>> cdm3 = new StrictMock<MockCDM>();
StrictMock<MockCrypto> crypto;
std::string appPackageName = "com.test.package";
const CdmClientPropertySet *propertySet = nullptr;
CdmIdentifier mCdmIdAtscModeSet;
// Generate ATSC keyset and license data
uint8_t atscKeySetIdRaw[kKeySetIdSize];
std::vector<uint8_t> licenseDataRaw;
const size_t kLicenseDataSize = 512;
licenseDataRaw.resize(kLicenseDataSize);
FILE *fp = fopen("/dev/urandom", "r");
ASSERT_NE(fp, nullptr) << "Failed to open /dev/urandom";
fread(atscKeySetIdRaw, sizeof(uint8_t), sizeof(atscKeySetIdRaw), fp);
fread(&licenseDataRaw[0], sizeof(uint8_t), licenseDataRaw.size(), fp);
fclose(fp);
std::string atscKeySetId(wvcdm::ATSC_KEY_SET_ID_PREFIX);
atscKeySetId.append(wvutil::HexEncode(&atscKeySetIdRaw[0], kKeySetIdSize));
atscKeySetId.resize(kKeySetIdSize);
memcpy(atscKeySetIdRaw, atscKeySetId.c_str(), sizeof(atscKeySetIdRaw));
std::string licenseDataBase64 = Base64Encode(licenseDataRaw);
std::string atscLicenseData = atscKeySetId + ":" + licenseDataBase64;
// Provide expected mock behavior
{
// No mock behavior expected for plugin1/cdm1
// For plugin2/cdm2
// Provide expected behavior in response to OpenSession and store the
// property set
EXPECT_CALL(*cdm2, OpenSession(_, _, _, _, _))
.WillOnce(DoAll(SetArgPointee<4>(mCdmSessionId),
SaveArg<1>(&propertySet),
SaveArg<2>(&mCdmIdAtscModeSet),
testing::Return(CdmResponseType(wvcdm::NO_ERROR))));
// Provide expected behavior when plugin2 requests session control info
EXPECT_CALL(*cdm2, QueryOemCryptoSessionId(mCdmSessionId, _))
.WillRepeatedly(Invoke(setSessionIdOnMap<4>));
EXPECT_CALL(*cdm2, CloseSession(_)).Times(1);
// For plugin3/cdm3
// Provide expected behavior in response to OpenSession and store the
// property set
EXPECT_CALL(*cdm3, OpenSession(_, _, _, _, _))
.WillOnce(DoAll(SetArgPointee<4>(mCdmSessionId),
SaveArg<1>(&propertySet),
SaveArg<2>(&mCdmIdAtscModeSet),
testing::Return(CdmResponseType(wvcdm::NO_ERROR))));
// Provide expected behavior when plugin3 requests session control info
EXPECT_CALL(*cdm3, QueryOemCryptoSessionId(mCdmSessionId, _))
.WillRepeatedly(Invoke(setSessionIdOnMap<4>));
EXPECT_CALL(*cdm3, CloseSession(_)).Times(1);
}
// Try storing an ATSC license without opening a session. Should fail.
std::shared_ptr<WVDrmPlugin> plugin1 =
::ndk::SharedRefBase::make<WVDrmPlugin>(
cdm1.get(), appPackageName.c_str(), &crypto, false);
auto ret = plugin1->setPropertyString(std::string("storeAtscLicense"),
atscLicenseData);
EXPECT_TRUE(!ret.isOk());
EXPECT_EQ(static_cast<int>(Status::ERROR_DRM_CANNOT_HANDLE),
ret.getServiceSpecificError());
// Try storing an ATSC license without setting ATSC mode. Should fail.
std::shared_ptr<WVDrmPlugin> plugin2 =
::ndk::SharedRefBase::make<WVDrmPlugin>(
cdm2.get(), appPackageName.c_str(), &crypto, false);
ret = plugin2->openSession(SecurityLevel::DEFAULT, &sessionId);
EXPECT_TRUE(ret.isOk());
ASSERT_THAT(propertySet, NotNull());
EXPECT_FALSE(propertySet->use_atsc_mode());
EXPECT_NE(mCdmIdAtscModeSet.app_package_name, wvcdm::ATSC_APP_PACKAGE_NAME);
ret = plugin2->setPropertyString(std::string("storeAtscLicense"),
atscLicenseData);
EXPECT_TRUE(!ret.isOk());
EXPECT_EQ(static_cast<int>(Status::ERROR_DRM_CANNOT_HANDLE),
ret.getServiceSpecificError());
ret = plugin2->closeSession(sessionId);
EXPECT_TRUE(ret.isOk());
// Try storing an ATSC license
// (a) Without ATSC key set prefix
// (b) Only ATSC key set
// (c) Only license data
// (d) Invalid Base64 license data
// (e) No ATSC key set
// (f) No license data
// All these should fail.
std::shared_ptr<WVDrmPlugin> plugin3 =
::ndk::SharedRefBase::make<WVDrmPlugin>(
cdm3.get(), appPackageName.c_str(), &crypto, false);
// Test turning on ATSC mode
ret = plugin3->setPropertyString(std::string("atscMode"),
std::string("enable"));
EXPECT_TRUE(ret.isOk());
ret = plugin3->openSession(SecurityLevel::DEFAULT, &sessionId);
EXPECT_TRUE(ret.isOk());
ASSERT_THAT(propertySet, NotNull());
EXPECT_TRUE(propertySet->use_atsc_mode());
EXPECT_EQ(mCdmIdAtscModeSet.app_package_name, wvcdm::ATSC_APP_PACKAGE_NAME);
// (a) No ATSC key set prefix
atscKeySetId.assign(wvutil::HexEncode(&atscKeySetIdRaw[0], kKeySetIdSize));
atscKeySetId.resize(kKeySetIdSize);
atscLicenseData = atscKeySetId + ":" + licenseDataBase64;
ret = plugin3->setPropertyString(std::string("storeAtscLicense"),
atscLicenseData);
EXPECT_TRUE(!ret.isOk());
EXPECT_EQ(static_cast<int>(Status::BAD_VALUE), ret.getServiceSpecificError());
// (b) Only ATSC key set
atscKeySetId.assign(wvcdm::ATSC_KEY_SET_ID_PREFIX);
atscKeySetId.append(wvutil::HexEncode(&atscKeySetIdRaw[0], kKeySetIdSize));
atscKeySetId.resize(kKeySetIdSize);
ret =
plugin3->setPropertyString(std::string("storeAtscLicense"), atscKeySetId);
EXPECT_TRUE(!ret.isOk());
// (c) Only license data
ret = plugin3->setPropertyString(std::string("storeAtscLicense"),
licenseDataBase64);
EXPECT_TRUE(!ret.isOk());
// (d) Invalid Base64 license data
licenseDataBase64.append("$");
atscLicenseData = atscKeySetId + ":" + licenseDataBase64;
ret = plugin3->setPropertyString(std::string("storeAtscLicense"),
atscLicenseData);
EXPECT_TRUE(!ret.isOk());
// (e) No ATSC key set
licenseDataBase64 = Base64Encode(licenseDataRaw);
atscLicenseData = ":" + licenseDataBase64;
ret = plugin3->setPropertyString(std::string("storeAtscLicense"),
atscLicenseData);
EXPECT_TRUE(!ret.isOk());
// (f) No license data
atscLicenseData = atscKeySetId + ":";
ret = plugin3->setPropertyString(std::string("storeAtscLicense"),
atscLicenseData);
EXPECT_TRUE(!ret.isOk());
ret = plugin3->closeSession(sessionId);
EXPECT_TRUE(ret.isOk());
}
} // namespace widevine
} // namespace drm
} // namespace hardware