Merge "Handle SPOID calculation for L3" into rvc-dev am: 25d1646138
Original change: https://googleplex-android-review.googlesource.com/c/platform/vendor/widevine/+/11736137 Change-Id: Ia4c431617789fbbb5f973f076b29408df2b2a2bc
This commit is contained in:
@@ -99,6 +99,9 @@ class WvContentDecryptionModule : public android::RefBase, public TimerHandler {
|
||||
virtual CdmResponseType Unprovision(CdmSecurityLevel level,
|
||||
const CdmIdentifier& identifier);
|
||||
|
||||
virtual bool IsProvisioned(CdmSecurityLevel level, const std::string& origin,
|
||||
const std::string& spoid, bool atsc_mode_enabled);
|
||||
|
||||
// Secure stop related methods
|
||||
virtual CdmResponseType GetUsageInfo(const std::string& app_id,
|
||||
const CdmIdentifier& identifier,
|
||||
|
||||
@@ -240,6 +240,18 @@ CdmResponseType WvContentDecryptionModule::Unprovision(
|
||||
return cdm_engine->Unprovision(level);
|
||||
}
|
||||
|
||||
bool WvContentDecryptionModule::IsProvisioned(CdmSecurityLevel security_level,
|
||||
const std::string& origin,
|
||||
const std::string& spoid,
|
||||
bool atsc_mode_enabled) {
|
||||
FileSystem file_system;
|
||||
file_system.set_origin(origin);
|
||||
file_system.set_identifier(spoid + origin);
|
||||
DeviceFiles device_files(&file_system);
|
||||
device_files.Init(security_level);
|
||||
return device_files.HasCertificate(atsc_mode_enabled);
|
||||
}
|
||||
|
||||
CdmResponseType WvContentDecryptionModule::GetUsageInfo(
|
||||
const std::string& app_id, const CdmIdentifier& identifier,
|
||||
CdmUsageInfo* usage_info) {
|
||||
|
||||
@@ -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