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:
Edwin Wong
2019-01-21 17:07:43 -08:00
parent 19c4996b3c
commit 54104c7a22
12 changed files with 462 additions and 30 deletions

View File

@@ -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