Handle SPOID calculation for L3
[ Merge of http://go/wvgerrit/101443 ] The WVDrmPlugin has a single CdmIdentifier. The CdmIdentifier contains a SPOID that is calculated from the device ID (keybox or OEM cert), an application reverse domain name and possibly an origin. The CdmIdentifier is set and SPOID calculated on certain calls into WVDrmPlugin. Once it is set, it will not be recalculated. We prevent certain operations such as modifying the origin once the CdmIdentifier has been set as this will require recalculating the SPOID. Recalculating the SPOID may affect open sessions or calls in progress. In a similar way, modifying the security level, will affect the Device ID value and in turn the SPOID. The security level cannot be modified if any sessions are open. This does leave open the possibility that the SPOID may be calculated at one security level, sessions are then closed, and the security level is then changed without an error being flagged. The provisioning certificate file name is based on the SPOID. When the SPOID does not match the security level, either the provisioning information may not be found even though that security level has been provisionined or the provisioning information may be stored in an incorrect location if provisioning occurs. The correct solution is to prevent modifications to the security level once the CdmIdentifier is set. This is a behavior change and might impact apps. We will reevaluate this for the next release. For now, we will work around this. When the CdmIdentifier is set for L3, we will calculate SPOIDs with both L1 and L3 device IDs and check if provisioning previously occurred with SPOIDs calculated for that level. If so, use that level, otherwise use L3. Bug: 147703382 Test: Android unit/integration tests, GtsMediaDrmTests Change-Id: Ia64adfc5848e431ee3876af03eebdb4b6eb83116
This commit is contained in:
@@ -413,12 +413,15 @@ struct WVDrmPlugin : public IDrmPlugin, IDrmPluginListener,
|
||||
const WVDrmPlugin& mParent;
|
||||
|
||||
Status calculateSpoid();
|
||||
Status calculateSpoid(const std::string& deviceID, std::string* spoid);
|
||||
|
||||
// Gets the device-unique ID from OEMCrypto. This must be private, since
|
||||
// this value must not be exposed to applications on SPOID devices. Code
|
||||
// outside this class should use getDeviceUniqueId() to get the
|
||||
// application-safe device-unique ID.
|
||||
Status getOemcryptoDeviceId(std::string* id);
|
||||
Status getOemcryptoDeviceId(wvcdm::SecurityLevel securityLevel,
|
||||
std::string* id);
|
||||
|
||||
// The unique identifier is meant to ensure that two clients with the
|
||||
// same spoid, origin and app package name still get different cdm engine
|
||||
@@ -450,6 +453,11 @@ struct WVDrmPlugin : public IDrmPlugin, IDrmPluginListener,
|
||||
Status queryProperty(const std::string& property,
|
||||
std::vector<uint8_t>& vector_value) const;
|
||||
|
||||
bool isProvisioned(wvcdm::CdmSecurityLevel securityLevel,
|
||||
const std::string& origin,
|
||||
const std::string& spoid,
|
||||
bool atsc_mode_enabled) const;
|
||||
|
||||
Status mapAndNotifyOfCdmResponseType(const std::vector<uint8_t>& sessionId,
|
||||
CdmResponseType res);
|
||||
|
||||
|
||||
@@ -2006,6 +2006,13 @@ Status WVDrmPlugin::queryProperty(const std::string& property,
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
bool WVDrmPlugin::isProvisioned(wvcdm::CdmSecurityLevel securityLevel,
|
||||
const std::string& origin,
|
||||
const std::string& spoid,
|
||||
bool atsc_mode_enabled) const {
|
||||
return mCDM->IsProvisioned(securityLevel, origin, spoid, atsc_mode_enabled);
|
||||
}
|
||||
|
||||
Status WVDrmPlugin::mapAndNotifyOfCdmResponseType(
|
||||
const std::vector<uint8_t>& sessionId, CdmResponseType res) {
|
||||
notifyOfCdmResponseType(sessionId, res);
|
||||
@@ -2189,23 +2196,97 @@ bool WVDrmPlugin::CdmIdentifierBuilder::set_use_atsc_mode(bool enable) {
|
||||
}
|
||||
|
||||
Status WVDrmPlugin::CdmIdentifierBuilder::calculateSpoid() {
|
||||
if (mUseSpoid) {
|
||||
std::string deviceId;
|
||||
if (!mUseSpoid)
|
||||
return Status::OK;
|
||||
|
||||
// Calculate SPOID for default security level if appropriate
|
||||
std::string deviceId;
|
||||
if (mParent.getRequestedSecurityLevel() == wvcdm::kLevelDefault) {
|
||||
Status res = getOemcryptoDeviceId(&deviceId);
|
||||
if (res != Status::OK) return res;
|
||||
|
||||
uint8_t hash[SHA256_DIGEST_LENGTH];
|
||||
SHA256_CTX ctx;
|
||||
SHA256_Init(&ctx);
|
||||
SHA256_Update(&ctx, deviceId.data(), deviceId.length());
|
||||
SHA256_Update(&ctx, mCdmIdentifier.app_package_name.data(),
|
||||
mCdmIdentifier.app_package_name.length());
|
||||
SHA256_Update(&ctx, origin().data(), origin().length());
|
||||
SHA256_Final(hash, &ctx);
|
||||
|
||||
mCdmIdentifier.spoid =
|
||||
std::string(reinterpret_cast<char*>(hash), SHA256_DIGEST_LENGTH);
|
||||
return calculateSpoid(deviceId, &mCdmIdentifier.spoid);
|
||||
}
|
||||
|
||||
// If requested security level is L3, possibilities are
|
||||
// (a) L3 has not been provisioned
|
||||
// (b) L3 was provisioned with L3 device ID in the CdmIdentifier
|
||||
// (c) L3 was provisioned (incorrectly) with L1 device ID in the CdmIdentifier
|
||||
// Check (b) first. Get L3 device ID, calculate SPOID and if provisioned
|
||||
// with this SPOID, return this SPOID.
|
||||
// Check (c) next. Get L1 device ID, calculate SPOID and if provisioned
|
||||
// with this SPOID, return this SPOID.
|
||||
// On any errors in (c) or not provisioned return L3 SPOID.
|
||||
Status res = getOemcryptoDeviceId(wvcdm::kLevel3, &deviceId);
|
||||
if (res != Status::OK) return res;
|
||||
|
||||
std::string spoidL3;
|
||||
res = calculateSpoid(deviceId, &spoidL3);
|
||||
if (res != Status::OK) return res;
|
||||
|
||||
bool atsc_mode_enabled =
|
||||
mCdmIdentifier.app_package_name == wvcdm::ATSC_APP_PACKAGE_NAME;
|
||||
|
||||
if (mParent.isProvisioned(wvcdm::kSecurityLevelL3, origin(), spoidL3,
|
||||
atsc_mode_enabled)) {
|
||||
mCdmIdentifier.spoid = spoidL3;
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
// Not provisioned with CdmIdentifier containing SPOID with L3 device ID.
|
||||
// Try SPOID with L1 device ID.
|
||||
std::string deviceIdLevelDefault;
|
||||
res = getOemcryptoDeviceId(wvcdm::kLevelDefault, &deviceIdLevelDefault);
|
||||
if (res != Status::OK) {
|
||||
mCdmIdentifier.spoid = spoidL3;
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
// If the L3 and default security level IDs are identical then the
|
||||
// device does not support L1.
|
||||
if (deviceId == deviceIdLevelDefault) {
|
||||
mCdmIdentifier.spoid = spoidL3;
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
std::string spoidLevelDefault;
|
||||
res = calculateSpoid(deviceIdLevelDefault, &spoidLevelDefault);
|
||||
if (res != Status::OK) {
|
||||
mCdmIdentifier.spoid = spoidL3;
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
if (mParent.isProvisioned(wvcdm::kSecurityLevelL1, origin(),
|
||||
spoidLevelDefault, atsc_mode_enabled)) {
|
||||
mCdmIdentifier.spoid = spoidLevelDefault;
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
// Not provisioned with CdmIdentifier containing SPOID with L1 or L3
|
||||
// device ID. Return L3 SPOID.
|
||||
mCdmIdentifier.spoid = spoidL3;
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
Status WVDrmPlugin::CdmIdentifierBuilder::calculateSpoid(
|
||||
const std::string& deviceId, std::string* spoid) {
|
||||
if (spoid == nullptr)
|
||||
return Status::ERROR_DRM_CANNOT_HANDLE;
|
||||
|
||||
if (!mUseSpoid) {
|
||||
spoid->clear();
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
uint8_t hash[SHA256_DIGEST_LENGTH];
|
||||
SHA256_CTX ctx;
|
||||
SHA256_Init(&ctx);
|
||||
SHA256_Update(&ctx, deviceId.data(), deviceId.length());
|
||||
SHA256_Update(&ctx, mAppPackageName.data(), mAppPackageName.length());
|
||||
SHA256_Update(&ctx, origin().data(), origin().length());
|
||||
SHA256_Final(hash, &ctx);
|
||||
|
||||
*spoid = std::string(reinterpret_cast<char*>(hash), SHA256_DIGEST_LENGTH);
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
@@ -2214,6 +2295,12 @@ Status WVDrmPlugin::CdmIdentifierBuilder::getOemcryptoDeviceId(
|
||||
return mParent.queryProperty(wvcdm::QUERY_KEY_DEVICE_ID, *id);
|
||||
}
|
||||
|
||||
Status WVDrmPlugin::CdmIdentifierBuilder::getOemcryptoDeviceId(
|
||||
wvcdm::SecurityLevel securityLevel,
|
||||
std::string* id) {
|
||||
return mParent.queryProperty(securityLevel, wvcdm::QUERY_KEY_DEVICE_ID, *id);
|
||||
}
|
||||
|
||||
uint32_t WVDrmPlugin::CdmIdentifierBuilder::getNextUniqueId() {
|
||||
// Start with 1. 0 is reserved for the default cdm identifier.
|
||||
static uint32_t unique_id = 1;
|
||||
|
||||
@@ -190,6 +190,9 @@ class MockCDM : public WvContentDecryptionModule {
|
||||
MOCK_METHOD2(Unprovision, CdmResponseType(CdmSecurityLevel,
|
||||
const CdmIdentifier&));
|
||||
|
||||
MOCK_METHOD3(IsProvisioned, bool(CdmSecurityLevel, const std::string&,
|
||||
const std::string&));
|
||||
|
||||
MOCK_METHOD3(GetUsageInfo, CdmResponseType(const std::string&,
|
||||
const CdmIdentifier&,
|
||||
CdmUsageInfo*));
|
||||
@@ -1681,6 +1684,350 @@ TEST_F(WVDrmPluginTest, CompliesWithSpoidVariability) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(WVDrmPluginTest, ReturnsSameL1Spoid) {
|
||||
StrictMock<MockCrypto> crypto;
|
||||
|
||||
std::string kL1DeviceId = kDeviceId + "L1";
|
||||
|
||||
const std::string kAppPackageName("com.google.widevine");
|
||||
constexpr size_t kSpoidQuery = 2;
|
||||
std::vector<uint8_t> spoid[kSpoidQuery];
|
||||
|
||||
android::sp<StrictMock<MockCDM>> cdm = new StrictMock<MockCDM>();
|
||||
|
||||
EXPECT_CALL(*cdm, QueryStatus(wvcdm::kLevelDefault, QUERY_KEY_DEVICE_ID, _))
|
||||
.Times(kSpoidQuery)
|
||||
.WillRepeatedly(DoAll(SetArgPointee<2>(kL1DeviceId),
|
||||
testing::Return(wvcdm::NO_ERROR)));
|
||||
|
||||
// Provide expected behavior to support session creation
|
||||
EXPECT_CALL(*cdm, OpenSession(StrEq("com.widevine"), _, _, _, _))
|
||||
.Times(kSpoidQuery)
|
||||
.WillRepeatedly(DoAll(SetArgPointee<4>(cdmSessionId),
|
||||
testing::Return(wvcdm::NO_ERROR)));
|
||||
|
||||
EXPECT_CALL(*cdm, QueryOemCryptoSessionId(cdmSessionId, _))
|
||||
.Times(kSpoidQuery)
|
||||
.WillRepeatedly(Invoke(setSessionIdOnMap<4>));
|
||||
|
||||
EXPECT_CALL(*cdm, CloseSession(_))
|
||||
.Times(kSpoidQuery);
|
||||
|
||||
// Open a session twice with the same security level, app package name and
|
||||
// origin and make sure the spoids returned are the same
|
||||
for (int i = 0; i < kSpoidQuery; ++i) {
|
||||
WVDrmPlugin plugin(cdm.get(), kAppPackageName, &crypto, true);
|
||||
|
||||
plugin.openSession([&](Status status, hidl_vec<uint8_t> hSessionId) {
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
sessionId.clear();
|
||||
sessionId.assign(hSessionId.data(),
|
||||
hSessionId.data() + hSessionId.size());
|
||||
});
|
||||
|
||||
EXPECT_EQ(Status::OK, plugin.closeSession(toHidlVec(sessionId)));
|
||||
|
||||
plugin.getPropertyByteArray(
|
||||
hidl_string("deviceUniqueId"),
|
||||
[&](Status status, hidl_vec<uint8_t> vectorResult) {
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
spoid[i] = vectorResult;
|
||||
});
|
||||
}
|
||||
|
||||
EXPECT_EQ(spoid[0], spoid[1]);
|
||||
}
|
||||
|
||||
TEST_F(WVDrmPluginTest, ReturnsL3SpoidsWhenL3ProvisionedUsingL3Spoid) {
|
||||
StrictMock<MockCrypto> crypto;
|
||||
|
||||
std::string kL3DeviceId = kDeviceId + "L3";
|
||||
|
||||
const std::string kAppPackageName("com.google.widevine");
|
||||
constexpr size_t kSpoidQuery = 2;
|
||||
std::vector<uint8_t> spoid[kSpoidQuery];
|
||||
|
||||
android::sp<StrictMock<MockCDM>> cdm = new StrictMock<MockCDM>();
|
||||
|
||||
EXPECT_CALL(*cdm, QueryStatus(wvcdm::kLevel3, QUERY_KEY_DEVICE_ID, _))
|
||||
.Times(kSpoidQuery)
|
||||
.WillRepeatedly(DoAll(SetArgPointee<2>(kL3DeviceId),
|
||||
testing::Return(wvcdm::NO_ERROR)));
|
||||
|
||||
// Provide expected behavior to support session creation
|
||||
EXPECT_CALL(*cdm, OpenSession(StrEq("com.widevine"), _, _, _, _))
|
||||
.Times(kSpoidQuery)
|
||||
.WillRepeatedly(DoAll(SetArgPointee<4>(cdmSessionId),
|
||||
testing::Return(wvcdm::NO_ERROR)));
|
||||
|
||||
EXPECT_CALL(*cdm, QueryOemCryptoSessionId(cdmSessionId, _))
|
||||
.Times(kSpoidQuery)
|
||||
.WillRepeatedly(Invoke(setSessionIdOnMap<4>));
|
||||
|
||||
EXPECT_CALL(*cdm, IsProvisioned(wvcdm::kSecurityLevelL3, _, _))
|
||||
.Times(kSpoidQuery)
|
||||
.WillRepeatedly(testing::Return(true));
|
||||
|
||||
EXPECT_CALL(*cdm, CloseSession(_))
|
||||
.Times(kSpoidQuery);
|
||||
|
||||
// The device is provisioned at L3 security level. Open a session twice
|
||||
// and make sure that the spoids returned are the same when the
|
||||
// session are opened at the same security level(L3), app package name
|
||||
// and origin
|
||||
for (size_t i = 0; i < kSpoidQuery; ++i) {
|
||||
WVDrmPlugin plugin(cdm.get(), kAppPackageName, &crypto, true);
|
||||
|
||||
// Forcing L3
|
||||
Status status = plugin.setPropertyString(hidl_string("securityLevel"),
|
||||
hidl_string("L3"));
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
|
||||
|
||||
plugin.openSession([&](Status status, hidl_vec<uint8_t> hSessionId) {
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
sessionId.clear();
|
||||
sessionId.assign(hSessionId.data(),
|
||||
hSessionId.data() + hSessionId.size());
|
||||
});
|
||||
|
||||
EXPECT_EQ(Status::OK, plugin.closeSession(toHidlVec(sessionId)));
|
||||
|
||||
plugin.getPropertyByteArray(
|
||||
hidl_string("deviceUniqueId"),
|
||||
[&](Status status, hidl_vec<uint8_t> vectorResult) {
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
spoid[i] = vectorResult;
|
||||
});
|
||||
}
|
||||
|
||||
EXPECT_EQ(spoid[0], spoid[1]);
|
||||
}
|
||||
|
||||
TEST_F(WVDrmPluginTest, ReturnsL3SpoidsWhenL3Unprovisioned) {
|
||||
StrictMock<MockCrypto> crypto;
|
||||
|
||||
std::string kL1DeviceId = kDeviceId + "L1";
|
||||
std::string kL3DeviceId = kDeviceId + "L3";
|
||||
|
||||
const std::string kAppPackageName("com.google.widevine");
|
||||
constexpr size_t kSpoidQuery = 2;
|
||||
std::vector<uint8_t> spoid[kSpoidQuery];
|
||||
|
||||
android::sp<StrictMock<MockCDM>> cdm = new StrictMock<MockCDM>();
|
||||
|
||||
EXPECT_CALL(*cdm, QueryStatus(wvcdm::kLevelDefault, QUERY_KEY_DEVICE_ID, _))
|
||||
.WillOnce(DoAll(SetArgPointee<2>(kL1DeviceId),
|
||||
testing::Return(wvcdm::NO_ERROR)));
|
||||
|
||||
EXPECT_CALL(*cdm, QueryStatus(wvcdm::kLevel3, QUERY_KEY_DEVICE_ID, _))
|
||||
.Times(kSpoidQuery)
|
||||
.WillRepeatedly(DoAll(SetArgPointee<2>(kL3DeviceId),
|
||||
testing::Return(wvcdm::NO_ERROR)));
|
||||
|
||||
// Provide expected behavior to support session creation
|
||||
EXPECT_CALL(*cdm, OpenSession(StrEq("com.widevine"), _, _, _, _))
|
||||
.Times(kSpoidQuery)
|
||||
.WillOnce(testing::Return(wvcdm::NEED_PROVISIONING))
|
||||
.WillOnce(DoAll(SetArgPointee<4>(cdmSessionId),
|
||||
testing::Return(wvcdm::NO_ERROR)));
|
||||
|
||||
EXPECT_CALL(*cdm, QueryOemCryptoSessionId(cdmSessionId, _))
|
||||
.WillOnce(Invoke(setSessionIdOnMap<4>));
|
||||
|
||||
EXPECT_CALL(*cdm, IsProvisioned(wvcdm::kSecurityLevelL3, _, _))
|
||||
.WillOnce(testing::Return(false))
|
||||
.WillOnce(testing::Return(true));
|
||||
|
||||
EXPECT_CALL(*cdm, IsProvisioned(wvcdm::kSecurityLevelL1, _, _))
|
||||
.WillOnce(testing::Return(false));
|
||||
|
||||
EXPECT_CALL(*cdm, CloseSession(_))
|
||||
.Times(1);
|
||||
|
||||
// This device is unprovisioned at the L3 security level. An attempt to
|
||||
// open a session results in an ERROR_DRM_NOT_PROVISIONED error.
|
||||
// Spoids are computed using device Unique IDs at each security level.
|
||||
// Since provisioning has not occurred for either spoid, the spoid
|
||||
// using the L3 device ID is used.
|
||||
android::sp<WVDrmPlugin> plugin = new WVDrmPlugin(cdm.get(), kAppPackageName,
|
||||
&crypto, true);
|
||||
|
||||
// Force L3
|
||||
Status status = plugin->setPropertyString(hidl_string("securityLevel"),
|
||||
hidl_string("L3"));
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
|
||||
plugin->openSession([&](Status status, hidl_vec<uint8_t>) {
|
||||
ASSERT_EQ(Status::ERROR_DRM_NOT_PROVISIONED, status);
|
||||
});
|
||||
|
||||
plugin->getPropertyByteArray(
|
||||
hidl_string("deviceUniqueId"),
|
||||
[&](Status status, hidl_vec<uint8_t> vectorResult) {
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
spoid[0] = vectorResult;
|
||||
});
|
||||
|
||||
// Try to open a session again. If provisioning took place, this time the
|
||||
// attempt will be successful. Retrieve the spoid. This time only
|
||||
// the device unique ID at the L3 security level will be queried.
|
||||
// Confirm that it matches the spoid queried earlier.
|
||||
plugin = new WVDrmPlugin(cdm.get(), kAppPackageName, &crypto, true);
|
||||
|
||||
// Force L3
|
||||
status = plugin->setPropertyString(hidl_string("securityLevel"),
|
||||
hidl_string("L3"));
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
|
||||
plugin->openSession([&](Status status, hidl_vec<uint8_t> hSessionId) {
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
sessionId.clear();
|
||||
sessionId.assign(hSessionId.data(),
|
||||
hSessionId.data() + hSessionId.size());
|
||||
});
|
||||
|
||||
EXPECT_EQ(Status::OK, plugin->closeSession(toHidlVec(sessionId)));
|
||||
|
||||
plugin->getPropertyByteArray(
|
||||
hidl_string("deviceUniqueId"),
|
||||
[&](Status status, hidl_vec<uint8_t> vectorResult) {
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
spoid[1] = vectorResult;
|
||||
});
|
||||
|
||||
EXPECT_EQ(spoid[0], spoid[1]);
|
||||
}
|
||||
|
||||
TEST_F(WVDrmPluginTest, ReturnsL1SpoidsWhenL3ProvisionedUsingL1Spoid) {
|
||||
StrictMock<MockCrypto> crypto;
|
||||
|
||||
std::string kL1DeviceId = kDeviceId + "L1";
|
||||
std::string kL3DeviceId = kDeviceId + "L3";
|
||||
|
||||
const std::string kAppPackageName("com.google.widevine");
|
||||
constexpr size_t kSpoidQuery = 2;
|
||||
std::vector<uint8_t> spoidL1;
|
||||
std::vector<uint8_t> spoidL3[kSpoidQuery];
|
||||
|
||||
// Set expectations for the first plugin instance
|
||||
android::sp<StrictMock<MockCDM>> cdm1 = new StrictMock<MockCDM>();
|
||||
|
||||
EXPECT_CALL(*cdm1, QueryStatus(wvcdm::kLevelDefault, QUERY_KEY_DEVICE_ID, _))
|
||||
.WillOnce(DoAll(SetArgPointee<2>(kL1DeviceId),
|
||||
testing::Return(wvcdm::NO_ERROR)));
|
||||
|
||||
// Provide expected behavior to support session creation
|
||||
EXPECT_CALL(*cdm1, OpenSession(StrEq("com.widevine"), _, _, _, _))
|
||||
.WillOnce(DoAll(SetArgPointee<4>(cdmSessionId),
|
||||
testing::Return(wvcdm::NO_ERROR)))
|
||||
.WillOnce(testing::Return(wvcdm::NEED_PROVISIONING));
|
||||
|
||||
EXPECT_CALL(*cdm1, QueryOemCryptoSessionId(cdmSessionId, _))
|
||||
.WillOnce(Invoke(setSessionIdOnMap<4>));
|
||||
|
||||
EXPECT_CALL(*cdm1, CloseSession(_));
|
||||
|
||||
// Open a session at L1 security level. The spoid is now computed with
|
||||
// the L1 device unique ID
|
||||
android::sp<WVDrmPlugin> plugin = new WVDrmPlugin(cdm1.get(), kAppPackageName,
|
||||
&crypto, true);
|
||||
|
||||
plugin->openSession([&](Status status, hidl_vec<uint8_t> hSessionId) {
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
sessionId.clear();
|
||||
sessionId.assign(hSessionId.data(),
|
||||
hSessionId.data() + hSessionId.size());
|
||||
});
|
||||
|
||||
EXPECT_EQ(Status::OK, plugin->closeSession(toHidlVec(sessionId)));
|
||||
|
||||
plugin->getPropertyByteArray(
|
||||
hidl_string("deviceUniqueId"),
|
||||
[&](Status status, hidl_vec<uint8_t> vectorResult) {
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
spoidL1 = vectorResult;
|
||||
});
|
||||
|
||||
// Force L3. This should not be allowed since the spoid has been
|
||||
// computed. We defer correcting this, as this might have an
|
||||
// impact on apps. L3 is not provisioned so a call to openSession
|
||||
// returns a ERROR_DRM_NOT_PROVISIONED error. No spoid computation takes
|
||||
// place.
|
||||
Status status = plugin->setPropertyString(hidl_string("securityLevel"),
|
||||
hidl_string("L3"));
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
|
||||
plugin->openSession([&](Status status, hidl_vec<uint8_t>) {
|
||||
ASSERT_EQ(Status::ERROR_DRM_NOT_PROVISIONED, status);
|
||||
});
|
||||
|
||||
plugin->getPropertyByteArray(
|
||||
hidl_string("deviceUniqueId"),
|
||||
[&](Status status, hidl_vec<uint8_t> vectorResult) {
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
spoidL3[0] = vectorResult;
|
||||
});
|
||||
|
||||
// Set expectations for the second plugin instance
|
||||
android::sp<StrictMock<MockCDM>> cdm2 = new StrictMock<MockCDM>();
|
||||
|
||||
EXPECT_CALL(*cdm2, QueryStatus(wvcdm::kLevelDefault, QUERY_KEY_DEVICE_ID, _))
|
||||
.WillOnce(DoAll(SetArgPointee<2>(kL1DeviceId),
|
||||
testing::Return(wvcdm::NO_ERROR)));
|
||||
|
||||
EXPECT_CALL(*cdm2, QueryStatus(wvcdm::kLevel3, QUERY_KEY_DEVICE_ID, _))
|
||||
.WillOnce(DoAll(SetArgPointee<2>(kL3DeviceId),
|
||||
testing::Return(wvcdm::NO_ERROR)));
|
||||
|
||||
// Provide expected behavior to support session creation
|
||||
EXPECT_CALL(*cdm2, OpenSession(StrEq("com.widevine"), _, _, _, _))
|
||||
.WillOnce(DoAll(SetArgPointee<4>(cdmSessionId),
|
||||
testing::Return(wvcdm::NO_ERROR)));
|
||||
|
||||
EXPECT_CALL(*cdm2, QueryOemCryptoSessionId(cdmSessionId, _))
|
||||
.WillOnce(Invoke(setSessionIdOnMap<4>));
|
||||
|
||||
EXPECT_CALL(*cdm2, IsProvisioned(wvcdm::kSecurityLevelL1, _, _))
|
||||
.WillOnce(testing::Return(true));
|
||||
|
||||
EXPECT_CALL(*cdm2, IsProvisioned(wvcdm::kSecurityLevelL3, _, _))
|
||||
.WillOnce(testing::Return(false));
|
||||
|
||||
EXPECT_CALL(*cdm2, CloseSession(_));
|
||||
|
||||
// Try to open a session again. If provisioning took place, this time the
|
||||
// attempt will be successful. Spoids are computed using device unique IDs
|
||||
// from both L1 and L3. The device is provisioned for L3 using the spoid with
|
||||
// L1 but not L3 device unique ID, so the spoid with L1 device unique ID is
|
||||
// used.
|
||||
plugin = new WVDrmPlugin(cdm2.get(), kAppPackageName, &crypto, true);
|
||||
|
||||
// Force L3
|
||||
status = plugin->setPropertyString(hidl_string("securityLevel"),
|
||||
hidl_string("L3"));
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
|
||||
plugin->openSession([&](Status status, hidl_vec<uint8_t> hSessionId) {
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
sessionId.clear();
|
||||
sessionId.assign(hSessionId.data(),
|
||||
hSessionId.data() + hSessionId.size());
|
||||
});
|
||||
|
||||
EXPECT_EQ(Status::OK, plugin->closeSession(toHidlVec(sessionId)));
|
||||
|
||||
plugin->getPropertyByteArray(
|
||||
hidl_string("deviceUniqueId"),
|
||||
[&](Status status, hidl_vec<uint8_t> vectorResult) {
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
spoidL3[1] = vectorResult;
|
||||
});
|
||||
|
||||
EXPECT_EQ(spoidL3[0], spoidL3[1]);
|
||||
EXPECT_EQ(spoidL1, spoidL3[0]);
|
||||
}
|
||||
|
||||
TEST_F(WVDrmPluginTest, FailsGenericMethodsWithoutAnAlgorithmSet) {
|
||||
android::sp<StrictMock<MockCDM>> cdm = new StrictMock<MockCDM>();
|
||||
StrictMock<MockCrypto> crypto;
|
||||
|
||||
Reference in New Issue
Block a user