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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user