Implement MediaDrm offline license support in Widevine hidl service.
Merged from http://go/wvgerrit/69723. The new APIs are getOfflineLicenseIds, getOfflineLicenseState and removeOfflineLicense. These methods are currently stubbed out in Widevine hidl service. This CL completes the implementation. Test: unit tests - libwvdrmdrmplugin_hidl_test Test: GTS --test com.google.android.media.gts.MediaDrmTest#testWidevineApi29 bug: 117570686 Change-Id: I96ffb75f453e36e931effefd3664b5faa8d69d30
This commit is contained in:
@@ -78,6 +78,7 @@ using wvcdm::CdmKeyRequest;
|
||||
using wvcdm::CdmKeySetId;
|
||||
using wvcdm::CdmKeySystem;
|
||||
using wvcdm::CdmLicenseType;
|
||||
using wvcdm::CdmOfflineLicenseState;
|
||||
using wvcdm::CdmProvisioningRequest;
|
||||
using wvcdm::CdmProvisioningResponse;
|
||||
using wvcdm::CdmQueryMap;
|
||||
@@ -159,7 +160,8 @@ class MockCDM : public WvContentDecryptionModule {
|
||||
MOCK_METHOD2(RestoreKey, CdmResponseType(const CdmSessionId&,
|
||||
const CdmKeySetId&));
|
||||
|
||||
MOCK_METHOD3(QueryStatus, CdmResponseType(wvcdm::SecurityLevel, const std::string&,
|
||||
MOCK_METHOD3(QueryStatus, CdmResponseType(wvcdm::SecurityLevel,
|
||||
const std::string&,
|
||||
std::string*));
|
||||
|
||||
MOCK_METHOD2(QueryKeyStatus, CdmResponseType(const CdmSessionId&,
|
||||
@@ -202,7 +204,24 @@ class MockCDM : public WvContentDecryptionModule {
|
||||
MOCK_METHOD2(GetMetrics, CdmResponseType(const CdmIdentifier&,
|
||||
drm_metrics::WvCdmMetrics*));
|
||||
|
||||
MOCK_METHOD2(GetDecryptHashError, CdmResponseType(const CdmSessionId&, std::string*));
|
||||
MOCK_METHOD2(GetDecryptHashError,
|
||||
CdmResponseType(const CdmSessionId&, std::string*));
|
||||
|
||||
MOCK_METHOD3(ListStoredLicenses,
|
||||
CdmResponseType(CdmSecurityLevel,
|
||||
const CdmIdentifier&,
|
||||
std::vector<std::string>*));
|
||||
|
||||
MOCK_METHOD4(GetOfflineLicenseState,
|
||||
CdmResponseType(const std::string&,
|
||||
CdmSecurityLevel,
|
||||
const CdmIdentifier&,
|
||||
CdmOfflineLicenseState*));
|
||||
|
||||
MOCK_METHOD3(RemoveOfflineLicense,
|
||||
CdmResponseType(const std::string&,
|
||||
CdmSecurityLevel,
|
||||
const CdmIdentifier&));
|
||||
};
|
||||
|
||||
class MockCrypto : public WVGenericCryptoInterface {
|
||||
@@ -260,8 +279,11 @@ MATCHER_P(HasOrigin, origin, "") {
|
||||
|
||||
class WVDrmPluginTest : public Test {
|
||||
protected:
|
||||
static const uint32_t kKeySetIdSize = 32;
|
||||
static const uint32_t kSessionIdSize = 16;
|
||||
uint8_t keySetIdRaw[kKeySetIdSize];
|
||||
uint8_t sessionIdRaw[kSessionIdSize];
|
||||
std::vector<uint8_t> keySetId;
|
||||
std::vector<uint8_t> sessionId;
|
||||
CdmSessionId cdmSessionId;
|
||||
|
||||
@@ -269,12 +291,19 @@ class WVDrmPluginTest : public Test {
|
||||
// Fill the session ID
|
||||
FILE* fp = fopen("/dev/urandom", "r");
|
||||
fread(sessionIdRaw, sizeof(uint8_t), kSessionIdSize, fp);
|
||||
fclose(fp);
|
||||
|
||||
memcpy(sessionIdRaw, SESSION_ID_PREFIX, sizeof(SESSION_ID_PREFIX) - 1);
|
||||
sessionId.assign(sessionIdRaw, sessionIdRaw + kSessionIdSize);
|
||||
cdmSessionId.assign(sessionId.begin(), sessionId.end());
|
||||
|
||||
fread(keySetIdRaw, sizeof(uint8_t), kKeySetIdSize, fp);
|
||||
fclose(fp);
|
||||
|
||||
memcpy(keySetIdRaw, KEY_SET_ID_PREFIX, sizeof(KEY_SET_ID_PREFIX));
|
||||
CdmKeySetId cdmKeySetId(reinterpret_cast<char*>(keySetIdRaw),
|
||||
kKeySetIdSize);
|
||||
keySetId.assign(keySetIdRaw, keySetIdRaw + kKeySetIdSize);
|
||||
|
||||
// Set default return values for gMock
|
||||
DefaultValue<CdmResponseType>::Set(wvcdm::NO_ERROR);
|
||||
DefaultValue<OEMCryptoResult>::Set(OEMCrypto_SUCCESS);
|
||||
@@ -728,15 +757,6 @@ TEST_F(WVDrmPluginTest, RestoresKeys) {
|
||||
StrictMock<MockCrypto> crypto;
|
||||
std::string appPackageName;
|
||||
|
||||
static const size_t kKeySetIdSize = 32;
|
||||
uint8_t keySetIdRaw[kKeySetIdSize];
|
||||
FILE* fp = fopen("/dev/urandom", "r");
|
||||
fread(keySetIdRaw, sizeof(uint8_t), kKeySetIdSize, fp);
|
||||
fclose(fp);
|
||||
|
||||
std::vector<uint8_t> keySetId;
|
||||
keySetId.assign(keySetIdRaw, keySetIdRaw + kKeySetIdSize);
|
||||
|
||||
EXPECT_CALL(*cdm, RestoreKey(cdmSessionId,
|
||||
ElementsAreArray(keySetIdRaw, kKeySetIdSize)))
|
||||
.Times(1);
|
||||
@@ -1039,7 +1059,7 @@ TEST_F(WVDrmPluginTest, GetsSecureStops) {
|
||||
.WillOnce(DoAll(SetArgPointee<2>(cdmStops),
|
||||
testing::Return(wvcdm::NO_ERROR)));
|
||||
|
||||
std::list<std::vector<uint8_t> > stops;
|
||||
std::vector<std::vector<uint8_t> > stops;
|
||||
|
||||
WVDrmPlugin plugin(cdm.get(), appPackageName, &crypto, false);
|
||||
Status status = plugin.setPropertyString(hidl_string("appId"),
|
||||
@@ -1058,17 +1078,11 @@ TEST_F(WVDrmPluginTest, GetsSecureStops) {
|
||||
}
|
||||
});
|
||||
|
||||
std::list<std::vector<uint8_t> >::iterator iter = stops.begin();
|
||||
uint32_t rawIter = 0;
|
||||
while (rawIter < kStopCount && iter != stops.end()) {
|
||||
EXPECT_THAT(*iter, ElementsAreArray(stopsRaw[rawIter], kStopSize));
|
||||
|
||||
++iter;
|
||||
++rawIter;
|
||||
size_t index = 0;
|
||||
for (auto stop : stops) {
|
||||
EXPECT_THAT(stop, ElementsAreArray(stopsRaw[index++], kStopSize));
|
||||
}
|
||||
// Assert that both lists are the same length
|
||||
EXPECT_EQ(kStopCount, rawIter);
|
||||
EXPECT_EQ(stops.end(), iter);
|
||||
EXPECT_EQ(kStopCount, index);
|
||||
}
|
||||
|
||||
TEST_F(WVDrmPluginTest, ReleasesAllSecureStops) {
|
||||
@@ -2715,6 +2729,124 @@ TEST_F(WVDrmPluginTest, DoesNotSetDecryptHashProperties) {
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
}
|
||||
|
||||
TEST_F(WVDrmPluginTest, GetOfflineLicenseIds) {
|
||||
android::sp<StrictMock<MockCDM>> cdm = new StrictMock<MockCDM>();
|
||||
StrictMock<MockCrypto> crypto;
|
||||
std::string appPackageName;
|
||||
|
||||
const uint32_t kLicenseCount = 5;
|
||||
|
||||
uint8_t mockIdsRaw[kLicenseCount * 2][kKeySetIdSize];
|
||||
FILE* fp = fopen("/dev/urandom", "r");
|
||||
for (uint32_t i = 0; i < kLicenseCount * 2; ++i) {
|
||||
fread(mockIdsRaw[i], sizeof(uint8_t), kKeySetIdSize, fp);
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
std::vector<std::string> mockIdsL1;
|
||||
for (uint32_t i = 0; i < kLicenseCount; ++i) {
|
||||
mockIdsL1.push_back(std::string(mockIdsRaw[i],
|
||||
mockIdsRaw[i] + kKeySetIdSize));
|
||||
}
|
||||
|
||||
std::vector<std::string> mockIdsL3;
|
||||
for (uint32_t i = 0; i < kLicenseCount; ++i) {
|
||||
mockIdsL3.push_back(std::string(mockIdsRaw[i+5],
|
||||
mockIdsRaw[i+5] + kKeySetIdSize));
|
||||
}
|
||||
|
||||
EXPECT_CALL(*cdm,
|
||||
ListStoredLicenses(kSecurityLevelL1, HasOrigin(EMPTY_ORIGIN), _))
|
||||
.WillOnce(DoAll(SetArgPointee<2>(mockIdsL1),
|
||||
testing::Return(wvcdm::NO_ERROR)));
|
||||
|
||||
EXPECT_CALL(*cdm,
|
||||
ListStoredLicenses(kSecurityLevelL3, HasOrigin(EMPTY_ORIGIN), _))
|
||||
.WillOnce(DoAll(SetArgPointee<2>(mockIdsL3),
|
||||
testing::Return(wvcdm::NO_ERROR)));
|
||||
|
||||
WVDrmPlugin plugin(cdm.get(), appPackageName, &crypto, false);
|
||||
|
||||
std::vector<std::vector<uint8_t> > offlineIds;
|
||||
offlineIds.clear();
|
||||
plugin.getOfflineLicenseKeySetIds(
|
||||
[&](Status status, hidl_vec<KeySetId> hKeySetIds) {
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
|
||||
std::vector<KeySetId> ids(hKeySetIds);
|
||||
|
||||
for (auto id : ids) {
|
||||
offlineIds.push_back(id);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
size_t index = 0;
|
||||
for (auto id : offlineIds) {
|
||||
EXPECT_THAT(id, ElementsAreArray(mockIdsRaw[index++], kKeySetIdSize));
|
||||
}
|
||||
EXPECT_EQ(kLicenseCount * 2, index);
|
||||
}
|
||||
|
||||
TEST_F(WVDrmPluginTest, GetOfflineLicenseState) {
|
||||
android::sp<StrictMock<MockCDM>> cdm = new StrictMock<MockCDM>();
|
||||
StrictMock<MockCrypto> crypto;
|
||||
std::string appPackageName;
|
||||
|
||||
EXPECT_CALL(*cdm, QueryStatus(_, QUERY_KEY_SECURITY_LEVEL, _))
|
||||
.WillRepeatedly(DoAll(SetArgPointee<2>(QUERY_VALUE_SECURITY_LEVEL_L1),
|
||||
testing::Return(wvcdm::NO_ERROR)));
|
||||
|
||||
EXPECT_CALL(*cdm,
|
||||
GetOfflineLicenseState(_, kSecurityLevelL1,
|
||||
HasOrigin(EMPTY_ORIGIN), _))
|
||||
.WillOnce(DoAll(SetArgPointee<3>(wvcdm::kLicenseStateActive),
|
||||
testing::Return(wvcdm::NO_ERROR)))
|
||||
.WillOnce(DoAll(SetArgPointee<3>(wvcdm::kLicenseStateReleasing),
|
||||
testing::Return(wvcdm::NO_ERROR)))
|
||||
.WillOnce(DoAll(SetArgPointee<3>(wvcdm::kLicenseStateUnknown),
|
||||
testing::Return(wvcdm::NO_ERROR)));
|
||||
|
||||
WVDrmPlugin plugin(cdm.get(), appPackageName, &crypto, false);
|
||||
Status status = plugin.setPropertyString(
|
||||
hidl_string("securityLevel"), hidl_string("L1"));
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
|
||||
plugin.getOfflineLicenseState(toHidlVec(keySetId),
|
||||
[&](Status status, OfflineLicenseState hLicenseState) {
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
ASSERT_EQ(OfflineLicenseState::USABLE, hLicenseState);
|
||||
});
|
||||
|
||||
plugin.getOfflineLicenseState(toHidlVec(keySetId),
|
||||
[&](Status status, OfflineLicenseState hLicenseState) {
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
ASSERT_EQ(OfflineLicenseState::INACTIVE, hLicenseState);
|
||||
});
|
||||
|
||||
plugin.getOfflineLicenseState(toHidlVec(keySetId),
|
||||
[&](Status status, OfflineLicenseState hLicenseState) {
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
ASSERT_EQ(OfflineLicenseState::UNKNOWN, hLicenseState);
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(WVDrmPluginTest, RemoveOfflineLicense) {
|
||||
android::sp<StrictMock<MockCDM>> cdm = new StrictMock<MockCDM>();
|
||||
StrictMock<MockCrypto> crypto;
|
||||
std::string appPackageName;
|
||||
|
||||
EXPECT_CALL(*cdm,
|
||||
RemoveOfflineLicense(_, kSecurityLevelL1,
|
||||
HasOrigin(EMPTY_ORIGIN)))
|
||||
.Times(1);
|
||||
|
||||
WVDrmPlugin plugin(cdm.get(), appPackageName, &crypto, false);
|
||||
|
||||
Status status = plugin.removeOfflineLicense(toHidlVec(keySetId));
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
} // namespace V1_2
|
||||
} // namespace drm
|
||||
|
||||
Reference in New Issue
Block a user