Add ATSC support - part 1
[ Merge of http://go/wvgerrit/100864 and http://go/ag/10704773 ] ATSC 3.0 allows for licenses to be downloaded OTA and are tied to a DRM certificate that may be shared across apps. The provisioning process for ATSC may happen at the factory or during an OS update. This contrasts from the regular OTT model, which requires that provisioning and license download have an uplink as well as a downlink connection. This adds support for the ATSC mode property. ATSC mode can only be set (or unset) before sessions are opened. Once the CDM identifier is set/sealed, requests to modify the ATSC mode will be rejected. If one needs to open sessions with both ATSC mode and regular (non-ATSC) mode, separate MediaDrm objects will need to be created. The default mode is to not use ATSC. Enable ATSC mode by calling mediaDrm.setPropertyString("atscMode", "enable") Disable ATSC mode by calling mediaDrm.setPropertyString("atscMode", "disable") Provisioning and unprovisioning requests for ATSC will be rejected as certificates will be retrieved by the ATSC service. Bug: 139730600 Test: WV unit/integration test, GtsMediaTestCases Change-Id: I142f286c711fe007ff42125c3c8cdc6450b6ea36
This commit is contained in:
@@ -190,7 +190,8 @@ class WVDrmPlugin : public android::DrmPlugin,
|
||||
class WVClientPropertySet : public wvcdm::CdmClientPropertySet {
|
||||
public:
|
||||
WVClientPropertySet()
|
||||
: mUsePrivacyMode(false), mShareKeys(false), mSessionSharingId(0) {}
|
||||
: mUsePrivacyMode(false), mShareKeys(false), mSessionSharingId(0),
|
||||
mUseAtscMode(false) {}
|
||||
|
||||
virtual ~WVClientPropertySet() {}
|
||||
|
||||
@@ -243,6 +244,14 @@ class WVDrmPlugin : public android::DrmPlugin,
|
||||
mAppId = appId;
|
||||
}
|
||||
|
||||
virtual bool use_atsc_mode() const {
|
||||
return mUseAtscMode;
|
||||
}
|
||||
|
||||
void set_use_atsc_mode(bool useAtscMode) {
|
||||
mUseAtscMode = useAtscMode;
|
||||
}
|
||||
|
||||
private:
|
||||
DISALLOW_EVIL_CONSTRUCTORS(WVClientPropertySet);
|
||||
|
||||
@@ -252,6 +261,7 @@ class WVDrmPlugin : public android::DrmPlugin,
|
||||
bool mShareKeys;
|
||||
uint32_t mSessionSharingId;
|
||||
std::string mAppId;
|
||||
bool mUseAtscMode;
|
||||
const std::string mEmptyString;
|
||||
} mPropertySet;
|
||||
|
||||
|
||||
@@ -287,7 +287,8 @@ struct WVDrmPlugin : public IDrmPlugin, IDrmPluginListener,
|
||||
class WVClientPropertySet : public wvcdm::CdmClientPropertySet {
|
||||
public:
|
||||
WVClientPropertySet()
|
||||
: mUsePrivacyMode(false), mShareKeys(false), mSessionSharingId(0) {}
|
||||
: mUsePrivacyMode(false), mShareKeys(false), mSessionSharingId(0),
|
||||
mUseAtscMode(false) {}
|
||||
|
||||
virtual ~WVClientPropertySet() {}
|
||||
|
||||
@@ -351,6 +352,14 @@ struct WVDrmPlugin : public IDrmPlugin, IDrmPluginListener,
|
||||
mAppId = appId;
|
||||
}
|
||||
|
||||
virtual bool use_atsc_mode() const {
|
||||
return mUseAtscMode;
|
||||
}
|
||||
|
||||
void set_use_atsc_mode(bool useAtscMode) {
|
||||
mUseAtscMode = useAtscMode;
|
||||
}
|
||||
|
||||
private:
|
||||
DISALLOW_EVIL_CONSTRUCTORS(WVClientPropertySet);
|
||||
|
||||
@@ -360,6 +369,7 @@ struct WVDrmPlugin : public IDrmPlugin, IDrmPluginListener,
|
||||
bool mShareKeys;
|
||||
uint32_t mSessionSharingId;
|
||||
std::string mAppId;
|
||||
bool mUseAtscMode;
|
||||
const std::string mEmptyString;
|
||||
} mPropertySet;
|
||||
|
||||
@@ -385,6 +395,9 @@ struct WVDrmPlugin : public IDrmPlugin, IDrmPluginListener,
|
||||
const std::string& origin() const { return mCdmIdentifier.origin; }
|
||||
bool set_origin(const std::string& id);
|
||||
|
||||
// This sets the app package name to allow apps to access ATSC licenses
|
||||
bool set_use_atsc_mode(bool enable);
|
||||
|
||||
// Indicates whether the builder can still be modified. This returns false
|
||||
// until a call to getCdmIdentifier.
|
||||
bool is_sealed() { return mIsIdentifierSealed; }
|
||||
|
||||
@@ -364,6 +364,10 @@ status_t WVDrmPlugin::getProvisionRequest(const String8& cert_type,
|
||||
CdmProvisioningRequest cdmProvisionRequest;
|
||||
string cdmDefaultUrl;
|
||||
|
||||
if (mPropertySet.use_atsc_mode()) {
|
||||
return mapCdmResponseType(wvcdm::PROVISIONING_NOT_ALLOWED_FOR_ATSC);
|
||||
}
|
||||
|
||||
CdmCertificateType cdmCertType = kCertificateWidevine;
|
||||
if (cert_type == "X.509") {
|
||||
cdmCertType = kCertificateX509;
|
||||
@@ -551,6 +555,12 @@ status_t WVDrmPlugin::getPropertyString(const String8& name,
|
||||
return queryProperty(QUERY_KEY_MAX_USAGE_TABLE_ENTRIES, value);
|
||||
} else if (name == "oemCryptoApiMinorVersion") {
|
||||
return queryProperty(QUERY_KEY_OEMCRYPTO_API_MINOR_VERSION, value);
|
||||
} else if (name == "atscMode") {
|
||||
if (mPropertySet.use_atsc_mode()) {
|
||||
value = kEnable;
|
||||
} else {
|
||||
value = kDisable;
|
||||
}
|
||||
} else {
|
||||
ALOGE("App requested unknown string property %s", name.string());
|
||||
return android::ERROR_DRM_CANNOT_HANDLE;
|
||||
@@ -663,6 +673,15 @@ status_t WVDrmPlugin::setPropertyString(const String8& name,
|
||||
return mapCdmResponseType(res);
|
||||
} else if (name == "decryptHashSessionId") {
|
||||
mDecryptHashSessionId = value.string();
|
||||
} else if (name == "atscMode") {
|
||||
if (value == kEnable) {
|
||||
mPropertySet.set_use_atsc_mode(true);
|
||||
} else if (value == kDisable) {
|
||||
mPropertySet.set_use_atsc_mode(false);
|
||||
} else {
|
||||
ALOGE("App requested unknown atsc mode %s", value.string());
|
||||
return android::BAD_VALUE;
|
||||
}
|
||||
} else {
|
||||
ALOGE("App set unknown string property %s", name.string());
|
||||
return android::ERROR_DRM_CANNOT_HANDLE;
|
||||
@@ -1087,6 +1106,10 @@ bool WVDrmPlugin::initDataResemblesPSSH(const Vector<uint8_t>& initData) {
|
||||
}
|
||||
|
||||
status_t WVDrmPlugin::unprovision(const CdmIdentifier& identifier) {
|
||||
if (mPropertySet.use_atsc_mode()) {
|
||||
return mapCdmResponseType(wvcdm::PROVISIONING_NOT_ALLOWED_FOR_ATSC);
|
||||
}
|
||||
|
||||
CdmResponseType res1 = mCDM->Unprovision(kSecurityLevelL1, identifier);
|
||||
CdmResponseType res3 = mCDM->Unprovision(kSecurityLevelL3, identifier);
|
||||
if (!isCdmResponseTypeSuccess(res1))
|
||||
|
||||
@@ -700,6 +700,12 @@ Return<void> WVDrmPlugin::getProvisionRequest_1_2(
|
||||
std::string defaultUrl;
|
||||
std::vector<uint8_t> request;
|
||||
|
||||
if (mPropertySet.use_atsc_mode()) {
|
||||
_hidl_cb(mapCdmResponseType_1_2(wvcdm::PROVISIONING_NOT_ALLOWED_FOR_ATSC),
|
||||
toHidlVec(request), hidl_string(defaultUrl));
|
||||
return Void();
|
||||
}
|
||||
|
||||
CdmIdentifier identifier;
|
||||
status = static_cast<Status_V1_2>(mCdmIdentifierBuilder.getCdmIdentifier(&identifier));
|
||||
if (status != Status_V1_2::OK) {
|
||||
@@ -1261,6 +1267,12 @@ Return<void> WVDrmPlugin::getPropertyString(const hidl_string& propertyName,
|
||||
status = queryProperty(wvcdm::QUERY_KEY_MAX_USAGE_TABLE_ENTRIES, value);
|
||||
} else if (name == "oemCryptoApiMinorVersion") {
|
||||
status = queryProperty(wvcdm::QUERY_KEY_OEMCRYPTO_API_MINOR_VERSION, value);
|
||||
} else if (name == "atscMode") {
|
||||
if (mPropertySet.use_atsc_mode()) {
|
||||
value = kEnable;
|
||||
} else {
|
||||
value = kDisable;
|
||||
}
|
||||
} else {
|
||||
ALOGE("App requested unknown string property %s", name.c_str());
|
||||
status = Status::ERROR_DRM_CANNOT_HANDLE;
|
||||
@@ -1413,6 +1425,23 @@ Return<Status> WVDrmPlugin::setPropertyString(const hidl_string& propertyName,
|
||||
return mapCdmResponseType(res);
|
||||
} else if (name == "decryptHashSessionId") {
|
||||
mDecryptHashSessionId = _value.c_str();
|
||||
} else if (name == "atscMode") {
|
||||
if (_value == kEnable) {
|
||||
if (!mCdmIdentifierBuilder.set_use_atsc_mode(true)) {
|
||||
ALOGE("Cdm identifier builder is sealed. Setting ATSC mode prohibited");
|
||||
return Status::BAD_VALUE;
|
||||
}
|
||||
mPropertySet.set_use_atsc_mode(true);
|
||||
} else if (_value == kDisable) {
|
||||
if (!mCdmIdentifierBuilder.set_use_atsc_mode(false)) {
|
||||
ALOGE("Cdm identifier builder is sealed. Setting ATSC mode prohibited");
|
||||
return Status::BAD_VALUE;
|
||||
}
|
||||
mPropertySet.set_use_atsc_mode(false);
|
||||
} else {
|
||||
ALOGE("App requested unknown ATSC mode %s", _value.c_str());
|
||||
return Status::BAD_VALUE;
|
||||
}
|
||||
} else {
|
||||
ALOGE("App set unknown string property %s", name.c_str());
|
||||
return Status::ERROR_DRM_CANNOT_HANDLE;
|
||||
@@ -2077,6 +2106,9 @@ bool WVDrmPlugin::initDataResemblesPSSH(const std::vector<uint8_t>& initData) {
|
||||
}
|
||||
|
||||
Status WVDrmPlugin::unprovision(const CdmIdentifier& identifier) {
|
||||
if (mPropertySet.use_atsc_mode())
|
||||
return mapCdmResponseType(wvcdm::PROVISIONING_NOT_ALLOWED_FOR_ATSC);
|
||||
|
||||
CdmResponseType res1 = mCDM->Unprovision(wvcdm::kSecurityLevelL1, identifier);
|
||||
CdmResponseType res3 = mCDM->Unprovision(wvcdm::kSecurityLevelL3, identifier);
|
||||
if (!isCdmResponseTypeSuccess(res1))
|
||||
@@ -2149,6 +2181,13 @@ bool WVDrmPlugin::CdmIdentifierBuilder::set_origin(const std::string& id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WVDrmPlugin::CdmIdentifierBuilder::set_use_atsc_mode(bool enable) {
|
||||
if (is_sealed()) return false;
|
||||
mCdmIdentifier.app_package_name =
|
||||
enable ? wvcdm::ATSC_APP_PACKAGE_NAME : mAppPackageName;
|
||||
return true;
|
||||
}
|
||||
|
||||
Status WVDrmPlugin::CdmIdentifierBuilder::calculateSpoid() {
|
||||
if (mUseSpoid) {
|
||||
std::string deviceId;
|
||||
@@ -2159,7 +2198,8 @@ Status WVDrmPlugin::CdmIdentifierBuilder::calculateSpoid() {
|
||||
SHA256_CTX ctx;
|
||||
SHA256_Init(&ctx);
|
||||
SHA256_Update(&ctx, deviceId.data(), deviceId.length());
|
||||
SHA256_Update(&ctx, mAppPackageName.data(), mAppPackageName.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);
|
||||
|
||||
|
||||
@@ -901,6 +901,23 @@ TEST_F(WVDrmPluginTest, GetsProvisioningRequests) {
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(WVDrmPluginTest, RejectsAtscProvisioningRequests) {
|
||||
android::sp<StrictMock<MockCDM>> cdm = new StrictMock<MockCDM>();
|
||||
StrictMock<MockCrypto> crypto;
|
||||
std::string appPackageName;
|
||||
|
||||
WVDrmPlugin plugin(cdm.get(), appPackageName, &crypto, false);
|
||||
|
||||
Status status = plugin.setPropertyString(hidl_string("atscMode"),
|
||||
hidl_string("enable"));
|
||||
|
||||
plugin.getProvisionRequest(
|
||||
hidl_string(""), hidl_string(""),
|
||||
[&](Status status, hidl_vec<uint8_t> , hidl_string ) {
|
||||
ASSERT_EQ(Status::ERROR_DRM_UNKNOWN, status);
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(WVDrmPluginTest, HandlesProvisioningResponses) {
|
||||
android::sp<StrictMock<MockCDM>> cdm = new StrictMock<MockCDM>();
|
||||
StrictMock<MockCrypto> crypto;
|
||||
@@ -1129,6 +1146,20 @@ TEST_F(WVDrmPluginTest, MuxesOriginUnprovisioningErrors) {
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(WVDrmPluginTest, RejectsAtscUnprovisionDeviceRequests) {
|
||||
android::sp<StrictMock<MockCDM>> cdm = new StrictMock<MockCDM>();
|
||||
StrictMock<MockCrypto> crypto;
|
||||
std::string appPackageName;
|
||||
|
||||
WVDrmPlugin plugin(cdm.get(), appPackageName, &crypto, false);
|
||||
|
||||
Status status = plugin.setPropertyString(hidl_string("atscMode"),
|
||||
hidl_string("enable"));
|
||||
|
||||
status = plugin.unprovisionDevice();
|
||||
ASSERT_EQ(Status::ERROR_DRM_UNKNOWN, status);
|
||||
}
|
||||
|
||||
TEST_F(WVDrmPluginTest, GetsSecureStops) {
|
||||
android::sp<StrictMock<MockCDM>> cdm = new StrictMock<MockCDM>();
|
||||
StrictMock<MockCrypto> crypto;
|
||||
@@ -2708,6 +2739,108 @@ TEST_F(WVDrmPluginTest, AllowsStoringOfSessionSharingId) {
|
||||
EXPECT_EQ(sharingId, propertySet->session_sharing_id());
|
||||
}
|
||||
|
||||
TEST_F(WVDrmPluginTest, CanSetAtscMode) {
|
||||
android::sp<StrictMock<MockCDM>> cdm = new StrictMock<MockCDM>();
|
||||
StrictMock<MockCrypto> crypto;
|
||||
std::string appPackageName = "com.test.package";
|
||||
|
||||
const CdmClientPropertySet* propertySet = nullptr;
|
||||
CdmIdentifier cdmIdAtscModeNotSet;
|
||||
CdmIdentifier cdmIdAtscModeSet;
|
||||
CdmIdentifier cdmIdAtscModeReset;
|
||||
|
||||
// Provide expected mock behavior
|
||||
{
|
||||
// Provide expected behavior in response to OpenSession and store the
|
||||
// property set
|
||||
EXPECT_CALL(*cdm, OpenSession(_, _, _, _, _))
|
||||
.WillOnce(DoAll(SetArgPointee<4>(cdmSessionId),
|
||||
SaveArg<1>(&propertySet),
|
||||
SaveArg<2>(&cdmIdAtscModeNotSet),
|
||||
testing::Return(wvcdm::NO_ERROR)))
|
||||
.WillOnce(DoAll(SetArgPointee<4>(cdmSessionId),
|
||||
SaveArg<1>(&propertySet),
|
||||
SaveArg<2>(&cdmIdAtscModeSet),
|
||||
testing::Return(wvcdm::NO_ERROR)))
|
||||
.WillOnce(DoAll(SetArgPointee<4>(cdmSessionId),
|
||||
SaveArg<1>(&propertySet),
|
||||
SaveArg<2>(&cdmIdAtscModeReset),
|
||||
testing::Return(wvcdm::NO_ERROR)));
|
||||
|
||||
// Provide expected behavior when plugin requests session control info
|
||||
EXPECT_CALL(*cdm, QueryOemCryptoSessionId(cdmSessionId, _))
|
||||
.WillRepeatedly(Invoke(setSessionIdOnMap<4>));
|
||||
|
||||
EXPECT_CALL(*cdm, CloseSession(_))
|
||||
.Times(AtLeast(0));
|
||||
}
|
||||
|
||||
WVDrmPlugin plugin1(cdm.get(), appPackageName, &crypto, false);
|
||||
|
||||
// Verify that ATSC mode is disabled by default
|
||||
plugin1.openSession([&](Status status, hidl_vec<uint8_t> hSessionId) {
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
sessionId.assign(hSessionId.data(), hSessionId.data() + hSessionId.size());
|
||||
});
|
||||
ASSERT_THAT(propertySet, NotNull());
|
||||
EXPECT_FALSE(propertySet->use_atsc_mode());
|
||||
EXPECT_EQ(cdmIdAtscModeNotSet.app_package_name, appPackageName);
|
||||
Status status = plugin1.closeSession(toHidlVec(sessionId));
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
|
||||
// Verify that ATSC mode can be enabled
|
||||
WVDrmPlugin plugin2(cdm.get(), appPackageName, &crypto, false);
|
||||
|
||||
// Test turning on ATSC mode
|
||||
status = plugin2.setPropertyString(hidl_string("atscMode"),
|
||||
hidl_string("enable"));
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
|
||||
plugin2.openSession([&](Status status, hidl_vec<uint8_t> hSessionId) {
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
sessionId.assign(hSessionId.data(), hSessionId.data() + hSessionId.size());
|
||||
});
|
||||
ASSERT_THAT(propertySet, NotNull());
|
||||
EXPECT_TRUE(propertySet->use_atsc_mode());
|
||||
EXPECT_EQ(cdmIdAtscModeSet.app_package_name, wvcdm::ATSC_APP_PACKAGE_NAME);
|
||||
status = plugin2.closeSession(toHidlVec(sessionId));
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
|
||||
|
||||
// Verify that ATSC mode can be enabled and disabled
|
||||
WVDrmPlugin plugin3(cdm.get(), appPackageName, &crypto, false);
|
||||
|
||||
// Test turning on ATSC mode
|
||||
status = plugin3.setPropertyString(hidl_string("atscMode"),
|
||||
hidl_string("enable"));
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
|
||||
// Test turning off ATSC mode
|
||||
status = plugin3.setPropertyString(hidl_string("atscMode"),
|
||||
hidl_string("disable"));
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
|
||||
plugin3.openSession([&](Status status, hidl_vec<uint8_t> hSessionId) {
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
sessionId.assign(hSessionId.data(), hSessionId.data() + hSessionId.size());
|
||||
});
|
||||
ASSERT_THAT(propertySet, NotNull());
|
||||
EXPECT_FALSE(propertySet->use_atsc_mode());
|
||||
EXPECT_EQ(cdmIdAtscModeReset.app_package_name, appPackageName);
|
||||
status = plugin3.closeSession(toHidlVec(sessionId));
|
||||
ASSERT_EQ(Status::OK, status);
|
||||
|
||||
// Test turning on and off ATSC mode. They should be rejected since the SPOID
|
||||
// has been calculated
|
||||
status = plugin3.setPropertyString(hidl_string("atscMode"),
|
||||
hidl_string("enable"));
|
||||
ASSERT_NE(Status::OK, status);
|
||||
|
||||
status = plugin3.setPropertyString(hidl_string("atscMode"),
|
||||
hidl_string("disable"));
|
||||
ASSERT_NE(Status::OK, status);
|
||||
}
|
||||
|
||||
TEST_F(WVDrmPluginTest, CanSetDecryptHashProperties) {
|
||||
android::sp<StrictMock<MockCDM>> cdm = new StrictMock<MockCDM>();
|
||||
StrictMock<MockCrypto> crypto;
|
||||
|
||||
Reference in New Issue
Block a user