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:
@@ -235,6 +235,10 @@ class CdmEngine {
|
|||||||
virtual CdmResponseType RemoveOfflineLicense(const std::string& key_set_id,
|
virtual CdmResponseType RemoveOfflineLicense(const std::string& key_set_id,
|
||||||
CdmSecurityLevel security_level);
|
CdmSecurityLevel security_level);
|
||||||
|
|
||||||
|
virtual CdmResponseType StoreAtscLicense(
|
||||||
|
RequestedSecurityLevel security_level, const CdmKeySetId& key_set_id,
|
||||||
|
const std::string& serialized_license_data);
|
||||||
|
|
||||||
// Usage related methods for streaming licenses
|
// Usage related methods for streaming licenses
|
||||||
// Retrieve a random usage info from the list of all usage infos for this app
|
// Retrieve a random usage info from the list of all usage infos for this app
|
||||||
// id. If |error_detail| is not null, an additional error code may be provided
|
// id. If |error_detail| is not null, an additional error code may be provided
|
||||||
|
|||||||
@@ -155,6 +155,9 @@ class DeviceFiles {
|
|||||||
virtual bool RetrieveLicense(const std::string& key_set_id,
|
virtual bool RetrieveLicense(const std::string& key_set_id,
|
||||||
CdmLicenseData* license_data,
|
CdmLicenseData* license_data,
|
||||||
ResponseType* result);
|
ResponseType* result);
|
||||||
|
virtual ResponseType StoreAtscLicense(
|
||||||
|
const CdmKeySetId& key_set_id,
|
||||||
|
const std::string& serialized_license_data);
|
||||||
|
|
||||||
virtual bool DeleteLicense(const std::string& key_set_id);
|
virtual bool DeleteLicense(const std::string& key_set_id);
|
||||||
virtual bool ListLicenses(std::vector<std::string>* key_set_ids);
|
virtual bool ListLicenses(std::vector<std::string>* key_set_ids);
|
||||||
|
|||||||
@@ -1413,6 +1413,35 @@ CdmResponseType CdmEngine::RemoveOfflineLicense(
|
|||||||
return sts;
|
return sts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CdmResponseType CdmEngine::StoreAtscLicense(
|
||||||
|
RequestedSecurityLevel requested_security_level,
|
||||||
|
const CdmKeySetId& key_set_id, const std::string& serialized_license_data) {
|
||||||
|
std::string security_level_string;
|
||||||
|
|
||||||
|
CdmResponseType status =
|
||||||
|
QueryStatus(requested_security_level, QUERY_KEY_SECURITY_LEVEL,
|
||||||
|
&security_level_string);
|
||||||
|
if (status != NO_ERROR) return status;
|
||||||
|
|
||||||
|
DeviceFiles handle(file_system_);
|
||||||
|
CdmSecurityLevel security_level =
|
||||||
|
security_level_string == QUERY_VALUE_SECURITY_LEVEL_L1 ? kSecurityLevelL1
|
||||||
|
: kSecurityLevelL3;
|
||||||
|
if (!handle.Init(security_level)) {
|
||||||
|
LOGE("Unable to initialize device files");
|
||||||
|
return CdmResponseType(STORE_ATSC_LICENSE_DEVICE_FILES_INIT_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceFiles::ResponseType response_type =
|
||||||
|
handle.StoreAtscLicense(key_set_id, serialized_license_data);
|
||||||
|
if (response_type != DeviceFiles::kNoError) {
|
||||||
|
LOGE("Unable to store ATSC license: response = %s",
|
||||||
|
DeviceFiles::ResponseTypeToString(response_type));
|
||||||
|
return CdmResponseType(STORE_ATSC_LICENSE_ERROR);
|
||||||
|
}
|
||||||
|
return CdmResponseType(NO_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
|
CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id,
|
||||||
const CdmSecureStopId& ssid,
|
const CdmSecureStopId& ssid,
|
||||||
int* error_detail,
|
int* error_detail,
|
||||||
|
|||||||
@@ -943,6 +943,11 @@ bool DeviceFiles::RetrieveLicense(const std::string& key_set_id,
|
|||||||
&license_data->wrapped_private_key);
|
&license_data->wrapped_private_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DeviceFiles::ResponseType DeviceFiles::StoreAtscLicense(
|
||||||
|
const CdmKeySetId& key_set_id, const std::string& serialized_data) {
|
||||||
|
return StoreFileWithHash(key_set_id + kLicenseFileNameExt, serialized_data);
|
||||||
|
}
|
||||||
|
|
||||||
bool DeviceFiles::DeleteLicense(const std::string& key_set_id) {
|
bool DeviceFiles::DeleteLicense(const std::string& key_set_id) {
|
||||||
RETURN_FALSE_IF_UNINITIALIZED();
|
RETURN_FALSE_IF_UNINITIALIZED();
|
||||||
return RemoveFile(key_set_id + kLicenseFileNameExt);
|
return RemoveFile(key_set_id + kLicenseFileNameExt);
|
||||||
|
|||||||
@@ -194,6 +194,10 @@ class WvContentDecryptionModule : public android::RefBase, public TimerHandler {
|
|||||||
virtual CdmResponseType GetSessionUserId(const CdmSessionId& session_id,
|
virtual CdmResponseType GetSessionUserId(const CdmSessionId& session_id,
|
||||||
uint32_t* user_id);
|
uint32_t* user_id);
|
||||||
|
|
||||||
|
virtual CdmResponseType StoreAtscLicense(
|
||||||
|
const CdmIdentifier& identifier, RequestedSecurityLevel security_level,
|
||||||
|
const CdmKeySetId& key_set_id,
|
||||||
|
const std::string& serialized_license_data);
|
||||||
virtual bool SetDefaultOtaKeyboxFallbackDurationRules();
|
virtual bool SetDefaultOtaKeyboxFallbackDurationRules();
|
||||||
virtual bool SetFastOtaKeyboxFallbackDurationRules();
|
virtual bool SetFastOtaKeyboxFallbackDurationRules();
|
||||||
|
|
||||||
|
|||||||
@@ -656,6 +656,15 @@ CdmResponseType WvContentDecryptionModule::GetSessionUserId(
|
|||||||
return CdmResponseType(NO_ERROR);
|
return CdmResponseType(NO_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CdmResponseType WvContentDecryptionModule::StoreAtscLicense(
|
||||||
|
const CdmIdentifier& identifier,
|
||||||
|
RequestedSecurityLevel requested_security_level,
|
||||||
|
const CdmKeySetId& key_set_id, const std::string& serialized_license_data) {
|
||||||
|
CdmEngine* cdm_engine = EnsureCdmForIdentifier(identifier);
|
||||||
|
return cdm_engine->StoreAtscLicense(requested_security_level, key_set_id,
|
||||||
|
serialized_license_data);
|
||||||
|
}
|
||||||
|
|
||||||
bool WvContentDecryptionModule::SetDefaultOtaKeyboxFallbackDurationRules() {
|
bool WvContentDecryptionModule::SetDefaultOtaKeyboxFallbackDurationRules() {
|
||||||
CdmEngine* cdm_engine = EnsureCdmForIdentifier(kDefaultCdmIdentifier);
|
CdmEngine* cdm_engine = EnsureCdmForIdentifier(kDefaultCdmIdentifier);
|
||||||
if (!cdm_engine) return false;
|
if (!cdm_engine) return false;
|
||||||
|
|||||||
@@ -456,6 +456,10 @@ class WVDrmPlugin : public ::aidl::android::hardware::drm::BnDrmPlugin,
|
|||||||
in_keyStatusList,
|
in_keyStatusList,
|
||||||
bool in_hasNewUsableKey);
|
bool in_hasNewUsableKey);
|
||||||
void sendSessionLostState(const std::vector<uint8_t>& in_sessionId);
|
void sendSessionLostState(const std::vector<uint8_t>& in_sessionId);
|
||||||
|
|
||||||
|
WvStatus parseAtscLicenseData(const std::string& value,
|
||||||
|
std::string* key_set_id,
|
||||||
|
std::string* serialized_license_data);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace widevine
|
} // namespace widevine
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
#include "mapErrors-inl.h"
|
#include "mapErrors-inl.h"
|
||||||
#include "media/stagefright/MediaErrors.h"
|
#include "media/stagefright/MediaErrors.h"
|
||||||
#include "openssl/sha.h"
|
#include "openssl/sha.h"
|
||||||
|
#include "string_conversions.h"
|
||||||
#include "wv_cdm_constants.h"
|
#include "wv_cdm_constants.h"
|
||||||
#include "wv_metrics.pb.h"
|
#include "wv_metrics.pb.h"
|
||||||
#include "wv_metrics_adapter.h"
|
#include "wv_metrics_adapter.h"
|
||||||
@@ -1344,6 +1345,32 @@ Status WVDrmPlugin::unprovisionDevice() {
|
|||||||
if (!success) {
|
if (!success) {
|
||||||
return toNdkScopedAStatus(Status::ERROR_DRM_UNKNOWN);
|
return toNdkScopedAStatus(Status::ERROR_DRM_UNKNOWN);
|
||||||
}
|
}
|
||||||
|
} else if (name == "storeAtscLicense") {
|
||||||
|
if (!mCdmIdentifierBuilder.is_sealed()) {
|
||||||
|
ALOGE("Cdm identifier builder is not sealed. Storing ATSC license "
|
||||||
|
"prohibited");
|
||||||
|
return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE);
|
||||||
|
}
|
||||||
|
if (!mPropertySet.use_atsc_mode()) {
|
||||||
|
ALOGE("ATSC mode not enabled. Storing ATSC license prohibited");
|
||||||
|
return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
CdmIdentifier identifier;
|
||||||
|
auto status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
|
||||||
|
if (status != Status::OK) {
|
||||||
|
ALOGE("Unable to get CDM Identifier = %d", status.get());
|
||||||
|
return toNdkScopedAStatus(status);
|
||||||
|
}
|
||||||
|
std::string key_set_id, license_data;
|
||||||
|
status = parseAtscLicenseData(_value, &key_set_id, &license_data);
|
||||||
|
if (status != Status::OK)
|
||||||
|
return toNdkScopedAStatus(status);
|
||||||
|
|
||||||
|
const CdmResponseType res = mCDM->StoreAtscLicense(
|
||||||
|
identifier, getRequestedSecurityLevel(), key_set_id, license_data);
|
||||||
|
|
||||||
|
return toNdkScopedAStatus(mapCdmResponseType(res));
|
||||||
} else {
|
} else {
|
||||||
ALOGE("App set unknown string property %s", name.c_str());
|
ALOGE("App set unknown string property %s", name.c_str());
|
||||||
return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE);
|
return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE);
|
||||||
@@ -1871,6 +1898,57 @@ bool WVDrmPlugin::isProvisioned(wvcdm::CdmSecurityLevel securityLevel,
|
|||||||
return mCDM->IsProvisioned(securityLevel, origin, spoid, atsc_mode_enabled);
|
return mCDM->IsProvisioned(securityLevel, origin, spoid, atsc_mode_enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WvStatus WVDrmPlugin::parseAtscLicenseData(
|
||||||
|
const std::string& in_value,
|
||||||
|
std::string* key_set_id,
|
||||||
|
std::string* serialized_license_data) {
|
||||||
|
if (key_set_id == nullptr) {
|
||||||
|
ALOGE("key_set_id null");
|
||||||
|
return WvStatus(Status::ERROR_DRM_CANNOT_HANDLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serialized_license_data == nullptr) {
|
||||||
|
ALOGE("serialized_license_data null");
|
||||||
|
return WvStatus(Status::ERROR_DRM_CANNOT_HANDLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_value.compare(0,
|
||||||
|
strlen(wvcdm::ATSC_KEY_SET_ID_PREFIX),
|
||||||
|
&wvcdm::ATSC_KEY_SET_ID_PREFIX[0]) != 0) {
|
||||||
|
ALOGE("ATSC license input does not conform to expectations. Key set does "
|
||||||
|
"not have a valid ATSC Key set prefix %s", in_value.c_str());
|
||||||
|
return WvStatus(Status::BAD_VALUE);
|
||||||
|
}
|
||||||
|
const char kColon = ':';
|
||||||
|
const size_t pos = in_value.find(kColon);
|
||||||
|
if (pos == std::string::npos) {
|
||||||
|
ALOGE("ATSC license input does not conform to expectations. Missing colon "
|
||||||
|
"= %s", in_value.c_str());
|
||||||
|
return WvStatus(Status::BAD_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos == in_value.length()) {
|
||||||
|
ALOGE("ATSC license input does not conform to expectations. No data after "
|
||||||
|
"colon");
|
||||||
|
return WvStatus(Status::BAD_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
*key_set_id = in_value.substr(0, pos);
|
||||||
|
const std::vector<uint8_t> license_data_binary =
|
||||||
|
wvutil::Base64Decode(in_value.substr(pos+1));
|
||||||
|
|
||||||
|
if (license_data_binary.empty()) {
|
||||||
|
ALOGE("ATSC license input does not conform to expectations. License data "
|
||||||
|
"failed to decode from Base64");
|
||||||
|
return WvStatus(Status::BAD_VALUE);
|
||||||
|
}
|
||||||
|
serialized_license_data->assign(license_data_binary.begin(),
|
||||||
|
license_data_binary.end());
|
||||||
|
|
||||||
|
return WvStatus(Status::OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
WvStatus WVDrmPlugin::mapAndNotifyOfCdmResponseType(
|
WvStatus WVDrmPlugin::mapAndNotifyOfCdmResponseType(
|
||||||
const vector<uint8_t>& sessionId, CdmResponseType res) {
|
const vector<uint8_t>& sessionId, CdmResponseType res) {
|
||||||
notifyOfCdmResponseType(sessionId, res);
|
notifyOfCdmResponseType(sessionId, res);
|
||||||
|
|||||||
@@ -239,6 +239,11 @@ public:
|
|||||||
MOCK_METHOD(CdmResponseType, RemoveOfflineLicense,
|
MOCK_METHOD(CdmResponseType, RemoveOfflineLicense,
|
||||||
(const std::string &, CdmSecurityLevel, const CdmIdentifier &),
|
(const std::string &, CdmSecurityLevel, const CdmIdentifier &),
|
||||||
(override));
|
(override));
|
||||||
|
|
||||||
|
MOCK_METHOD(CdmResponseType, StoreAtscLicense,
|
||||||
|
(const CdmIdentifier &, wvcdm::RequestedSecurityLevel,
|
||||||
|
const std::string &, const std::string &),
|
||||||
|
(override));
|
||||||
};
|
};
|
||||||
|
|
||||||
class MockCrypto : public WVGenericCryptoInterface {
|
class MockCrypto : public WVGenericCryptoInterface {
|
||||||
@@ -2909,6 +2914,253 @@ TEST_F(WVDrmPluginHalTest, RemoveOfflineLicense) {
|
|||||||
EXPECT_TRUE(ret.isOk());
|
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 widevine
|
||||||
} // namespace drm
|
} // namespace drm
|
||||||
} // namespace hardware
|
} // namespace hardware
|
||||||
|
|||||||
Reference in New Issue
Block a user