From bbe9f6afc4b5ebbb68d3500f6004fb6cb3f1a876 Mon Sep 17 00:00:00 2001 From: Rahul Frias Date: Mon, 16 Mar 2020 18:18:46 -0700 Subject: [PATCH] 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 --- .../core/include/cdm_client_property_set.h | 1 + .../cdm/core/include/wv_cdm_constants.h | 1 + .../cdm/core/include/wv_cdm_types.h | 4 +- libwvdrmengine/cdm/core/src/cdm_engine.cpp | 1 + .../cdm_engine_metrics_decorator_unittest.cpp | 2 + .../cdm/core/test/license_request.h | 4 +- .../test/service_certificate_unittest.cpp | 1 + .../cdm/core/test/test_printers.cpp | 3 + .../cdm/test/cdm_extended_duration_test.cpp | 8 +- .../cdm/test/request_license_test.cpp | 6 +- libwvdrmengine/include/WVErrors.h | 7 +- libwvdrmengine/include/mapErrors-inl.h | 2 + libwvdrmengine/include_hidl/mapErrors-inl.h | 1 + libwvdrmengine/mediadrm/include/WVDrmPlugin.h | 12 +- .../mediadrm/include_hidl/WVDrmPlugin.h | 15 +- libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp | 23 +++ .../mediadrm/src_hidl/WVDrmPlugin.cpp | 42 +++++- .../mediadrm/test/WVDrmPlugin_test.cpp | 133 ++++++++++++++++++ 18 files changed, 256 insertions(+), 10 deletions(-) diff --git a/libwvdrmengine/cdm/core/include/cdm_client_property_set.h b/libwvdrmengine/cdm/core/include/cdm_client_property_set.h index 38398ebe..e6c7ba4c 100644 --- a/libwvdrmengine/cdm/core/include/cdm_client_property_set.h +++ b/libwvdrmengine/cdm/core/include/cdm_client_property_set.h @@ -24,6 +24,7 @@ class CdmClientPropertySet { virtual uint32_t session_sharing_id() const = 0; virtual void set_session_sharing_id(uint32_t id) = 0; virtual const std::string& app_id() const = 0; + virtual bool use_atsc_mode() const = 0; }; } // namespace wvcdm diff --git a/libwvdrmengine/cdm/core/include/wv_cdm_constants.h b/libwvdrmengine/cdm/core/include/wv_cdm_constants.h index 5d427e20..0c152b07 100644 --- a/libwvdrmengine/cdm/core/include/wv_cdm_constants.h +++ b/libwvdrmengine/cdm/core/include/wv_cdm_constants.h @@ -45,6 +45,7 @@ static const uint32_t OEM_CRYPTO_API_VERSION_SUPPORTS_RESOURCE_RATING_TIER = 15; static const char SESSION_ID_PREFIX[] = "sid"; static const char KEY_SET_ID_PREFIX[] = "ksid"; static const char KEY_SYSTEM[] = "com.widevine"; +static const char ATSC_APP_PACKAGE_NAME[] = "org.atsc"; // define query keys, values here static const std::string QUERY_KEY_LICENSE_TYPE = diff --git a/libwvdrmengine/cdm/core/include/wv_cdm_types.h b/libwvdrmengine/cdm/core/include/wv_cdm_types.h index 7d87adfe..c06ed7e5 100644 --- a/libwvdrmengine/cdm/core/include/wv_cdm_types.h +++ b/libwvdrmengine/cdm/core/include/wv_cdm_types.h @@ -408,10 +408,12 @@ enum CdmResponseType { CANNOT_DECRYPT_ZERO_SUBSAMPLES = 354, SAMPLE_AND_SUBSAMPLE_SIZE_MISMATCH = 355, INVALID_IV_SIZE = 356, - LOAD_USAGE_ENTRY_INVALID_SESSION = 357, + PROVISIONING_NOT_ALLOWED_FOR_ATSC = 357, + // 357 was |LOAD_USAGE_ENTRY_INVALID_SESSION| in early R builds MOVE_USAGE_ENTRY_DESTINATION_IN_USE = 358, SHRINK_USAGE_TABLE_HEADER_ENTRY_IN_USE = 359, LICENSE_USAGE_ENTRY_MISSING = 360, + LOAD_USAGE_ENTRY_INVALID_SESSION = 361, // Don't forget to add new values to // * core/test/test_printers.cpp. // * android/include/mapErrors-inl.h diff --git a/libwvdrmengine/cdm/core/src/cdm_engine.cpp b/libwvdrmengine/cdm/core/src/cdm_engine.cpp index 66309966..c18dc339 100644 --- a/libwvdrmengine/cdm/core/src/cdm_engine.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_engine.cpp @@ -62,6 +62,7 @@ class UsagePropertySet : public CdmClientPropertySet { void set_session_sharing_id(uint32_t /* id */) override {} const std::string& app_id() const override { return app_id_; } void set_app_id(const std::string& appId) { app_id_ = appId; } + bool use_atsc_mode() const override { return false; } private: std::string app_id_; diff --git a/libwvdrmengine/cdm/core/test/cdm_engine_metrics_decorator_unittest.cpp b/libwvdrmengine/cdm/core/test/cdm_engine_metrics_decorator_unittest.cpp index 6d09be08..4b9d2d4b 100644 --- a/libwvdrmengine/cdm/core/test/cdm_engine_metrics_decorator_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/cdm_engine_metrics_decorator_unittest.cpp @@ -38,6 +38,8 @@ class MockCdmClientPropertySet : public CdmClientPropertySet { MOCK_CONST_METHOD0(is_session_sharing_enabled, bool()); MOCK_CONST_METHOD0(session_sharing_id, uint32_t()); MOCK_METHOD1(set_session_sharing_id, void(uint32_t)); + MOCK_CONST_METHOD0(use_atsc_mode, bool()); + MOCK_METHOD1(set_use_atsc_mode, void(bool)); MOCK_CONST_METHOD0(app_id, const std::string&()); }; diff --git a/libwvdrmengine/cdm/core/test/license_request.h b/libwvdrmengine/cdm/core/test/license_request.h index 93254c1e..6188d8e4 100644 --- a/libwvdrmengine/cdm/core/test/license_request.h +++ b/libwvdrmengine/cdm/core/test/license_request.h @@ -15,8 +15,8 @@ namespace wvcdm { // Google license servers. class LicenseRequest { public: - LicenseRequest() {}; - ~LicenseRequest() {}; + LicenseRequest() {} + ~LicenseRequest() {} void GetDrmMessage(const std::string& response, std::string& drm_msg); diff --git a/libwvdrmengine/cdm/core/test/service_certificate_unittest.cpp b/libwvdrmengine/cdm/core/test/service_certificate_unittest.cpp index aecbc92a..6958d307 100644 --- a/libwvdrmengine/cdm/core/test/service_certificate_unittest.cpp +++ b/libwvdrmengine/cdm/core/test/service_certificate_unittest.cpp @@ -79,6 +79,7 @@ class StubCdmClientPropertySet : public CdmClientPropertySet { } uint32_t session_sharing_id() const override { return session_sharing_id_; } + virtual bool use_atsc_mode() const { return false; } void set_session_sharing_id(uint32_t id) override { session_sharing_id_ = id; diff --git a/libwvdrmengine/cdm/core/test/test_printers.cpp b/libwvdrmengine/cdm/core/test/test_printers.cpp index 25bb54b9..6caa8931 100644 --- a/libwvdrmengine/cdm/core/test/test_printers.cpp +++ b/libwvdrmengine/cdm/core/test/test_printers.cpp @@ -935,6 +935,9 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) { case WEBM_INIT_DATA_UNAVAILABLE: *os << "WEBM_INIT_DATA_UNAVAILABLE"; break; + case PROVISIONING_NOT_ALLOWED_FOR_ATSC: + *os << "PROVISIONING_NOT_ALLOWED_FOR_ATSC"; + break; default: *os << "Unknown CdmResponseType"; break; diff --git a/libwvdrmengine/cdm/test/cdm_extended_duration_test.cpp b/libwvdrmengine/cdm/test/cdm_extended_duration_test.cpp index caf0ae10..d7fd97ba 100644 --- a/libwvdrmengine/cdm/test/cdm_extended_duration_test.cpp +++ b/libwvdrmengine/cdm/test/cdm_extended_duration_test.cpp @@ -198,7 +198,8 @@ class TestWvCdmClientPropertySet : public CdmClientPropertySet { TestWvCdmClientPropertySet() : use_privacy_mode_(false), is_session_sharing_enabled_(false), - session_sharing_id_(0) {} + session_sharing_id_(0), + use_atsc_mode_(false) {} virtual ~TestWvCdmClientPropertySet() {} virtual const std::string& app_id() const { return app_id_; } @@ -214,6 +215,7 @@ class TestWvCdmClientPropertySet : public CdmClientPropertySet { return is_session_sharing_enabled_; } virtual uint32_t session_sharing_id() const { return session_sharing_id_; } + virtual bool use_atsc_mode() const { return use_atsc_mode_; } void set_app_id(const std::string& app_id) { app_id_ = app_id; } void set_security_level(const std::string& security_level) { @@ -229,6 +231,9 @@ class TestWvCdmClientPropertySet : public CdmClientPropertySet { is_session_sharing_enabled_ = enable; } void set_session_sharing_id(uint32_t id) { session_sharing_id_ = id; } + void set_use_atsc_mode(bool use_atsc_mode) { + use_atsc_mode_ = use_atsc_mode; + } private: std::string app_id_; @@ -237,6 +242,7 @@ class TestWvCdmClientPropertySet : public CdmClientPropertySet { bool use_privacy_mode_; bool is_session_sharing_enabled_; uint32_t session_sharing_id_; + bool use_atsc_mode_; }; class WvCdmExtendedDurationTest : public WvCdmTestBase { diff --git a/libwvdrmengine/cdm/test/request_license_test.cpp b/libwvdrmengine/cdm/test/request_license_test.cpp index f3c562af..a5da0c50 100644 --- a/libwvdrmengine/cdm/test/request_license_test.cpp +++ b/libwvdrmengine/cdm/test/request_license_test.cpp @@ -1608,7 +1608,8 @@ class TestWvCdmClientPropertySet : public CdmClientPropertySet { TestWvCdmClientPropertySet() : use_privacy_mode_(false), is_session_sharing_enabled_(false), - session_sharing_id_(0) {} + session_sharing_id_(0), + use_atsc_mode_(false) {} virtual ~TestWvCdmClientPropertySet() {} virtual const std::string& app_id() const { return app_id_; } @@ -1624,6 +1625,7 @@ class TestWvCdmClientPropertySet : public CdmClientPropertySet { return is_session_sharing_enabled_; } virtual uint32_t session_sharing_id() const { return session_sharing_id_; } + virtual bool use_atsc_mode() const { return use_atsc_mode_; } void set_app_id(const std::string& app_id) { app_id_ = app_id; } void set_security_level(const std::string& security_level) { @@ -1639,6 +1641,7 @@ class TestWvCdmClientPropertySet : public CdmClientPropertySet { is_session_sharing_enabled_ = enable; } void set_session_sharing_id(uint32_t id) { session_sharing_id_ = id; } + void set_use_atsc_mode(bool use_atsc_mode) { use_atsc_mode_ = use_atsc_mode; } private: std::string app_id_; @@ -1647,6 +1650,7 @@ class TestWvCdmClientPropertySet : public CdmClientPropertySet { bool use_privacy_mode_; bool is_session_sharing_enabled_; uint32_t session_sharing_id_; + bool use_atsc_mode_; }; class TestWvCdmEventListener : public WvCdmEventListener { diff --git a/libwvdrmengine/include/WVErrors.h b/libwvdrmengine/include/WVErrors.h index d0bf031c..9cfb2228 100644 --- a/libwvdrmengine/include/WVErrors.h +++ b/libwvdrmengine/include/WVErrors.h @@ -289,14 +289,17 @@ enum { kCannotDecryptZeroSubsamples = ERROR_DRM_VENDOR_MIN + 306, kSampleAndSubsampleSizeMismatch = ERROR_DRM_VENDOR_MIN + 307, kInvalidIvSize = ERROR_DRM_VENDOR_MIN + 308, - kLoadUsageEntryInvalidSession = ERROR_DRM_VENDOR_MIN + 309, + kProvisioningNotAllowedForAtsc = ERROR_DRM_VENDOR_MIN + 309, + // ERROR_DRM_VENDOR_MIN + 309 was |kLoadUsageEntryInvalidSession| + // in early R builds kMoveUsageEntryDestinationInUse = ERROR_DRM_VENDOR_MIN + 310, kShrinkUsageTableHeaderEntryInUse = ERROR_DRM_VENDOR_MIN + 311, kLicenseUsageEntryMissing = ERROR_DRM_VENDOR_MIN + 312, + kLoadUsageEntryInvalidSession = ERROR_DRM_VENDOR_MIN + 313, // This should always follow the last error code. // The offset value should be updated each time a new error code is added. - kErrorWVDrmMaxErrorUsed = ERROR_DRM_VENDOR_MIN + 312, + kErrorWVDrmMaxErrorUsed = ERROR_DRM_VENDOR_MIN + 313, // Used by crypto test mode kErrorTestMode = ERROR_DRM_VENDOR_MAX, diff --git a/libwvdrmengine/include/mapErrors-inl.h b/libwvdrmengine/include/mapErrors-inl.h index c86726ed..e7315d7f 100644 --- a/libwvdrmengine/include/mapErrors-inl.h +++ b/libwvdrmengine/include/mapErrors-inl.h @@ -442,6 +442,8 @@ static android::status_t mapCdmResponseType(wvcdm::CdmResponseType res) { return kPrivacyModeError2; case wvcdm::PRIVACY_MODE_ERROR_3: return kPrivacyModeError3; + case wvcdm::PROVISIONING_NOT_ALLOWED_FOR_ATSC: + return kProvisioningNotAllowedForAtsc; case wvcdm::REFRESH_KEYS_ERROR: return kRefreshKeysError; case wvcdm::REINIT_ERROR: diff --git a/libwvdrmengine/include_hidl/mapErrors-inl.h b/libwvdrmengine/include_hidl/mapErrors-inl.h index dfd4daa4..c6f61778 100644 --- a/libwvdrmengine/include_hidl/mapErrors-inl.h +++ b/libwvdrmengine/include_hidl/mapErrors-inl.h @@ -359,6 +359,7 @@ static Status mapCdmResponseType(wvcdm::CdmResponseType res) { case wvcdm::MOVE_USAGE_ENTRY_DESTINATION_IN_USE: case wvcdm::SHRINK_USAGE_TABLE_HEADER_ENTRY_IN_USE: case wvcdm::LICENSE_USAGE_ENTRY_MISSING: + case wvcdm::PROVISIONING_NOT_ALLOWED_FOR_ATSC: ALOGW("Returns UNKNOWN error for legacy status: %d", res); return Status::ERROR_DRM_UNKNOWN; diff --git a/libwvdrmengine/mediadrm/include/WVDrmPlugin.h b/libwvdrmengine/mediadrm/include/WVDrmPlugin.h index 6b9fa6d5..3284a901 100644 --- a/libwvdrmengine/mediadrm/include/WVDrmPlugin.h +++ b/libwvdrmengine/mediadrm/include/WVDrmPlugin.h @@ -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; diff --git a/libwvdrmengine/mediadrm/include_hidl/WVDrmPlugin.h b/libwvdrmengine/mediadrm/include_hidl/WVDrmPlugin.h index 8f0608a8..06cf4d59 100644 --- a/libwvdrmengine/mediadrm/include_hidl/WVDrmPlugin.h +++ b/libwvdrmengine/mediadrm/include_hidl/WVDrmPlugin.h @@ -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; } diff --git a/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp b/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp index ccb01a65..70164ae4 100644 --- a/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp +++ b/libwvdrmengine/mediadrm/src/WVDrmPlugin.cpp @@ -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& 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)) diff --git a/libwvdrmengine/mediadrm/src_hidl/WVDrmPlugin.cpp b/libwvdrmengine/mediadrm/src_hidl/WVDrmPlugin.cpp index 205f842e..d14d6ed3 100644 --- a/libwvdrmengine/mediadrm/src_hidl/WVDrmPlugin.cpp +++ b/libwvdrmengine/mediadrm/src_hidl/WVDrmPlugin.cpp @@ -700,6 +700,12 @@ Return WVDrmPlugin::getProvisionRequest_1_2( std::string defaultUrl; std::vector 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(mCdmIdentifierBuilder.getCdmIdentifier(&identifier)); if (status != Status_V1_2::OK) { @@ -1261,6 +1267,12 @@ Return 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 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& 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); diff --git a/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp b/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp index 2dbb8669..6d04209f 100644 --- a/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp +++ b/libwvdrmengine/mediadrm/test/WVDrmPlugin_test.cpp @@ -901,6 +901,23 @@ TEST_F(WVDrmPluginTest, GetsProvisioningRequests) { }); } +TEST_F(WVDrmPluginTest, RejectsAtscProvisioningRequests) { + android::sp> cdm = new StrictMock(); + StrictMock 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 , hidl_string ) { + ASSERT_EQ(Status::ERROR_DRM_UNKNOWN, status); + }); +} + TEST_F(WVDrmPluginTest, HandlesProvisioningResponses) { android::sp> cdm = new StrictMock(); StrictMock crypto; @@ -1129,6 +1146,20 @@ TEST_F(WVDrmPluginTest, MuxesOriginUnprovisioningErrors) { }); } +TEST_F(WVDrmPluginTest, RejectsAtscUnprovisionDeviceRequests) { + android::sp> cdm = new StrictMock(); + StrictMock 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> cdm = new StrictMock(); StrictMock crypto; @@ -2708,6 +2739,108 @@ TEST_F(WVDrmPluginTest, AllowsStoringOfSessionSharingId) { EXPECT_EQ(sharingId, propertySet->session_sharing_id()); } +TEST_F(WVDrmPluginTest, CanSetAtscMode) { + android::sp> cdm = new StrictMock(); + StrictMock 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 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 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 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> cdm = new StrictMock(); StrictMock crypto;