summaryrefslogtreecommitdiffstats
path: root/drm
diff options
context:
space:
mode:
Diffstat (limited to 'drm')
-rw-r--r--drm/1.0/Android.bp16
-rw-r--r--drm/1.0/default/Android.mk38
-rw-r--r--drm/1.0/default/CryptoPlugin.cpp1
-rw-r--r--drm/1.0/default/android.hardware.drm@1.0-service-lazy.rc10
-rw-r--r--drm/1.0/default/android.hardware.drm@1.0-service.rc2
-rw-r--r--drm/1.0/default/common_default_service.mk45
-rw-r--r--drm/1.0/default/service.cpp10
-rw-r--r--drm/1.0/default/serviceLazy.cpp40
-rw-r--r--drm/1.0/vts/functional/Android.bp1
-rw-r--r--drm/1.0/vts/functional/drm_hal_vendor_test.cpp32
-rw-r--r--drm/1.1/Android.bp7
-rw-r--r--drm/1.1/README.md84
-rw-r--r--drm/1.1/vts/functional/Android.bp1
-rw-r--r--drm/1.1/vts/functional/drm_hal_clearkey_test.cpp12
-rw-r--r--drm/1.2/Android.bp24
-rw-r--r--drm/1.2/ICryptoFactory.hal35
-rw-r--r--drm/1.2/ICryptoPlugin.hal84
-rw-r--r--drm/1.2/IDrmFactory.hal51
-rw-r--r--drm/1.2/IDrmPlugin.hal247
-rw-r--r--drm/1.2/IDrmPluginListener.hal57
-rw-r--r--drm/1.2/types.hal118
-rw-r--r--drm/1.2/vts/functional/Android.bp40
-rw-r--r--drm/1.2/vts/functional/drm_hal_clearkey_module.cpp130
-rw-r--r--drm/1.2/vts/functional/drm_hal_clearkey_module.h61
-rw-r--r--drm/1.2/vts/functional/drm_hal_common.cpp496
-rw-r--r--drm/1.2/vts/functional/drm_hal_common.h204
-rw-r--r--drm/1.2/vts/functional/drm_hal_test.cpp591
-rw-r--r--drm/1.2/vts/functional/vendor_modules.cpp72
-rw-r--r--drm/1.2/vts/functional/vendor_modules.h73
29 files changed, 2500 insertions, 82 deletions
diff --git a/drm/1.0/Android.bp b/drm/1.0/Android.bp
index aca5ae492..fea851f54 100644
--- a/drm/1.0/Android.bp
+++ b/drm/1.0/Android.bp
@@ -17,22 +17,6 @@ hidl_interface {
interfaces: [
"android.hidl.base@1.0",
],
- types: [
- "BufferType",
- "DestinationBuffer",
- "EventType",
- "KeyRequestType",
- "KeyStatus",
- "KeyStatusType",
- "KeyType",
- "KeyValue",
- "Mode",
- "Pattern",
- "SecureStop",
- "SharedBuffer",
- "Status",
- "SubSample",
- ],
gen_java: false,
}
diff --git a/drm/1.0/default/Android.mk b/drm/1.0/default/Android.mk
index 99773be38..d66f377b5 100644
--- a/drm/1.0/default/Android.mk
+++ b/drm/1.0/default/Android.mk
@@ -19,39 +19,23 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
+
+include $(LOCAL_PATH)/common_default_service.mk
LOCAL_MODULE := android.hardware.drm@1.0-service
LOCAL_INIT_RC := android.hardware.drm@1.0-service.rc
-LOCAL_PROPRIETARY_MODULE := true
-LOCAL_MODULE_RELATIVE_PATH := hw
-LOCAL_SRC_FILES := \
- service.cpp \
+LOCAL_SRC_FILES := service.cpp
-LOCAL_SHARED_LIBRARIES := \
- android.hardware.drm@1.0 \
- android.hidl.memory@1.0 \
- libhidlbase \
- libhidltransport \
- libhardware \
- liblog \
- libutils \
- libbinder \
-
-LOCAL_STATIC_LIBRARIES := \
- android.hardware.drm@1.0-helper \
+include $(BUILD_EXECUTABLE)
-LOCAL_C_INCLUDES := \
- hardware/interfaces/drm
+############# Build legacy drm lazy service ############
-LOCAL_HEADER_LIBRARIES := \
- media_plugin_headers
+include $(CLEAR_VARS)
-# TODO(b/18948909) Some legacy DRM plugins only support 32-bit. They need to be
-# migrated to 64-bit. Once all of a device's legacy DRM plugins support 64-bit,
-# that device can turn on TARGET_ENABLE_MEDIADRM_64 to build this service as
-# 64-bit.
-ifneq ($(TARGET_ENABLE_MEDIADRM_64), true)
-LOCAL_32_BIT_ONLY := true
-endif
+include $(LOCAL_PATH)/common_default_service.mk
+LOCAL_MODULE := android.hardware.drm@1.0-service-lazy
+LOCAL_OVERRIDES_MODULES := android.hardware.drm@1.0-service
+LOCAL_INIT_RC := android.hardware.drm@1.0-service-lazy.rc
+LOCAL_SRC_FILES := serviceLazy.cpp
include $(BUILD_EXECUTABLE)
diff --git a/drm/1.0/default/CryptoPlugin.cpp b/drm/1.0/default/CryptoPlugin.cpp
index 4fe6c9b43..8ddc38022 100644
--- a/drm/1.0/default/CryptoPlugin.cpp
+++ b/drm/1.0/default/CryptoPlugin.cpp
@@ -52,7 +52,6 @@ namespace implementation {
Return<void> CryptoPlugin::setSharedBufferBase(const hidl_memory& base,
uint32_t bufferId) {
sp<IMemory> hidlMemory = mapMemory(base);
- ALOGE_IF(hidlMemory == nullptr, "mapMemory returns nullptr");
// allow mapMemory to return nullptr
mSharedBufferMap[bufferId] = hidlMemory;
diff --git a/drm/1.0/default/android.hardware.drm@1.0-service-lazy.rc b/drm/1.0/default/android.hardware.drm@1.0-service-lazy.rc
new file mode 100644
index 000000000..4b32f7f1f
--- /dev/null
+++ b/drm/1.0/default/android.hardware.drm@1.0-service-lazy.rc
@@ -0,0 +1,10 @@
+service vendor.drm-hal-1-0 /vendor/bin/hw/android.hardware.drm@1.0-service-lazy
+ interface android.hardware.drm@1.0::ICryptoFactory default
+ interface android.hardware.drm@1.0::IDrmFactory default
+ oneshot
+ disabled
+ class hal
+ user media
+ group mediadrm drmrpc
+ ioprio rt 4
+ writepid /dev/cpuset/foreground/tasks
diff --git a/drm/1.0/default/android.hardware.drm@1.0-service.rc b/drm/1.0/default/android.hardware.drm@1.0-service.rc
index a3457b523..790ededbe 100644
--- a/drm/1.0/default/android.hardware.drm@1.0-service.rc
+++ b/drm/1.0/default/android.hardware.drm@1.0-service.rc
@@ -1,4 +1,6 @@
service vendor.drm-hal-1-0 /vendor/bin/hw/android.hardware.drm@1.0-service
+ interface android.hardware.drm@1.0::ICryptoFactory default
+ interface android.hardware.drm@1.0::IDrmFactory default
class hal
user media
group mediadrm drmrpc
diff --git a/drm/1.0/default/common_default_service.mk b/drm/1.0/default/common_default_service.mk
new file mode 100644
index 000000000..28db567a9
--- /dev/null
+++ b/drm/1.0/default/common_default_service.mk
@@ -0,0 +1,45 @@
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+include $(CLEAR_VARS)
+LOCAL_PROPRIETARY_MODULE := true
+LOCAL_MODULE_RELATIVE_PATH := hw
+
+LOCAL_SHARED_LIBRARIES := \
+ android.hardware.drm@1.0 \
+ android.hidl.memory@1.0 \
+ libhidlbase \
+ libhidltransport \
+ libhardware \
+ liblog \
+ libutils \
+ libbinder \
+
+LOCAL_STATIC_LIBRARIES := \
+ android.hardware.drm@1.0-helper \
+
+LOCAL_C_INCLUDES := \
+ hardware/interfaces/drm
+
+LOCAL_HEADER_LIBRARIES := \
+ media_plugin_headers
+
+# TODO(b/18948909) Some legacy DRM plugins only support 32-bit. They need to be
+# migrated to 64-bit. Once all of a device's legacy DRM plugins support 64-bit,
+# that device can turn on TARGET_ENABLE_MEDIADRM_64 to build this service as
+# 64-bit.
+ifneq ($(TARGET_ENABLE_MEDIADRM_64), true)
+LOCAL_32_BIT_ONLY := true
+endif
diff --git a/drm/1.0/default/service.cpp b/drm/1.0/default/service.cpp
index 1a44ce225..98d2c3b54 100644
--- a/drm/1.0/default/service.cpp
+++ b/drm/1.0/default/service.cpp
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#define LOG_TAG "android.hardware.drm@1.0-service"
#include <1.0/default/CryptoFactory.h>
#include <1.0/default/DrmFactory.h>
@@ -31,15 +30,8 @@ using android::hardware::drm::V1_0::ICryptoFactory;
using android::hardware::drm::V1_0::IDrmFactory;
int main() {
- ALOGD("android.hardware.drm@1.0-service starting...");
-
- // The DRM HAL may communicate to other vendor components via
- // /dev/vndbinder
- android::ProcessState::initWithDriver("/dev/vndbinder");
-
configureRpcThreadpool(8, true /* callerWillJoin */);
- android::status_t status =
- registerPassthroughServiceImplementation<IDrmFactory>();
+ android::status_t status = registerPassthroughServiceImplementation<IDrmFactory>();
LOG_ALWAYS_FATAL_IF(
status != android::OK,
"Error while registering drm service: %d", status);
diff --git a/drm/1.0/default/serviceLazy.cpp b/drm/1.0/default/serviceLazy.cpp
new file mode 100644
index 000000000..e5068b569
--- /dev/null
+++ b/drm/1.0/default/serviceLazy.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <1.0/default/CryptoFactory.h>
+#include <1.0/default/DrmFactory.h>
+
+#include <hidl/HidlTransportSupport.h>
+#include <hidl/LegacySupport.h>
+
+#include <binder/ProcessState.h>
+
+using android::hardware::configureRpcThreadpool;
+using android::hardware::joinRpcThreadpool;
+using android::hardware::registerLazyPassthroughServiceImplementation;
+
+using android::hardware::drm::V1_0::ICryptoFactory;
+using android::hardware::drm::V1_0::IDrmFactory;
+
+int main() {
+ configureRpcThreadpool(8, true /* callerWillJoin */);
+ android::status_t status = registerLazyPassthroughServiceImplementation<IDrmFactory>();
+ LOG_ALWAYS_FATAL_IF(status != android::OK, "Error while registering drm service: %d", status);
+ status = registerLazyPassthroughServiceImplementation<ICryptoFactory>();
+ LOG_ALWAYS_FATAL_IF(status != android::OK, "Error while registering crypto service: %d",
+ status);
+ joinRpcThreadpool();
+}
diff --git a/drm/1.0/vts/functional/Android.bp b/drm/1.0/vts/functional/Android.bp
index 57678fb46..d6ebfdddf 100644
--- a/drm/1.0/vts/functional/Android.bp
+++ b/drm/1.0/vts/functional/Android.bp
@@ -32,4 +32,5 @@ cc_test {
"libssl",
"libcrypto",
],
+ test_suites: ["general-tests"],
}
diff --git a/drm/1.0/vts/functional/drm_hal_vendor_test.cpp b/drm/1.0/vts/functional/drm_hal_vendor_test.cpp
index d03b2af37..20a2ca4af 100644
--- a/drm/1.0/vts/functional/drm_hal_vendor_test.cpp
+++ b/drm/1.0/vts/functional/drm_hal_vendor_test.cpp
@@ -177,7 +177,7 @@ class DrmHalVendorFactoryTest : public testing::TestWithParam<std::string> {
TEST_P(DrmHalVendorFactoryTest, ValidateConfigurations) {
const char* kVendorStr = "Vendor module ";
size_t count = 0;
- for (auto config : contentConfigurations) {
+ for (const auto& config : contentConfigurations) {
ASSERT_TRUE(config.name.size() > 0) << kVendorStr << "has no name";
ASSERT_TRUE(config.serverUrl.size() > 0) << kVendorStr
<< "has no serverUrl";
@@ -186,7 +186,7 @@ TEST_P(DrmHalVendorFactoryTest, ValidateConfigurations) {
ASSERT_TRUE(config.mimeType.size() > 0) << kVendorStr
<< "has no mime type";
ASSERT_TRUE(config.keys.size() >= 1) << kVendorStr << "has no keys";
- for (auto key : config.keys) {
+ for (const auto& key : config.keys) {
ASSERT_TRUE(key.keyId.size() > 0) << kVendorStr
<< " has zero length keyId";
ASSERT_TRUE(key.keyId.size() > 0) << kVendorStr
@@ -245,7 +245,7 @@ TEST_P(DrmHalVendorFactoryTest, InvalidContentTypeNotSupported) {
*/
TEST_P(DrmHalVendorFactoryTest, ValidContentTypeSupported) {
RETURN_IF_SKIPPED;
- for (auto config : contentConfigurations) {
+ for (const auto& config : contentConfigurations) {
EXPECT_TRUE(drmFactory->isContentTypeSupported(config.mimeType));
}
}
@@ -610,7 +610,7 @@ TEST_P(DrmHalVendorPluginTest, RemoveKeysNewSession) {
*/
TEST_P(DrmHalVendorPluginTest, RestoreKeys) {
RETURN_IF_SKIPPED;
- for (auto config : contentConfigurations) {
+ for (const auto& config : contentConfigurations) {
if (config.policy.allowOffline) {
auto sessionId = openSession();
hidl_vec<uint8_t> keySetId =
@@ -645,7 +645,7 @@ TEST_P(DrmHalVendorPluginTest, RestoreKeysNull) {
*/
TEST_P(DrmHalVendorPluginTest, RestoreKeysClosedSession) {
RETURN_IF_SKIPPED;
- for (auto config : contentConfigurations) {
+ for (const auto& config : contentConfigurations) {
if (config.policy.allowOffline) {
auto sessionId = openSession();
hidl_vec<uint8_t> keySetId =
@@ -1022,8 +1022,8 @@ TEST_P(DrmHalVendorPluginTest, RequiresSecureDecoderInvalidMimeType) {
*/
TEST_P(DrmHalVendorPluginTest, RequiresSecureDecoderConfig) {
RETURN_IF_SKIPPED;
- for (auto config : contentConfigurations) {
- for (auto key : config.keys) {
+ for (const auto& config : contentConfigurations) {
+ for (const auto& key : config.keys) {
if (key.isSecure) {
EXPECT_TRUE(cryptoPlugin->requiresSecureDecoderComponent(config.mimeType));
break;
@@ -1471,7 +1471,7 @@ TEST_P(DrmHalVendorDecryptTest, QueryKeyStatusWithNoKeys) {
*/
TEST_P(DrmHalVendorDecryptTest, QueryKeyStatus) {
RETURN_IF_SKIPPED;
- for (auto config : contentConfigurations) {
+ for (const auto& config : contentConfigurations) {
auto sessionId = openSession();
loadKeys(sessionId, config);
auto keyStatus = queryKeyStatus(sessionId);
@@ -1485,8 +1485,8 @@ TEST_P(DrmHalVendorDecryptTest, QueryKeyStatus) {
*/
TEST_P(DrmHalVendorDecryptTest, ClearSegmentTest) {
RETURN_IF_SKIPPED;
- for (auto config : contentConfigurations) {
- for (auto key : config.keys) {
+ for (const auto& config : contentConfigurations) {
+ for (const auto& key : config.keys) {
const size_t kSegmentSize = 1024;
vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
const Pattern noPattern = {0, 0};
@@ -1513,8 +1513,8 @@ TEST_P(DrmHalVendorDecryptTest, ClearSegmentTest) {
*/
TEST_P(DrmHalVendorDecryptTest, EncryptedAesCtrSegmentTest) {
RETURN_IF_SKIPPED;
- for (auto config : contentConfigurations) {
- for (auto key : config.keys) {
+ for (const auto& config : contentConfigurations) {
+ for (const auto& key : config.keys) {
const size_t kSegmentSize = 1024;
vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
const Pattern noPattern = {0, 0};
@@ -1540,8 +1540,8 @@ TEST_P(DrmHalVendorDecryptTest, EncryptedAesCtrSegmentTest) {
*/
TEST_P(DrmHalVendorDecryptTest, EncryptedAesCtrSegmentTestNoKeys) {
RETURN_IF_SKIPPED;
- for (auto config : contentConfigurations) {
- for (auto key : config.keys) {
+ for (const auto& config : contentConfigurations) {
+ for (const auto& key : config.keys) {
vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
const Pattern noPattern = {0, 0};
const vector<SubSample> subSamples = {{.numBytesOfClearData = 256,
@@ -1567,8 +1567,8 @@ TEST_P(DrmHalVendorDecryptTest, EncryptedAesCtrSegmentTestNoKeys) {
*/
TEST_P(DrmHalVendorDecryptTest, AttemptDecryptWithKeysRemoved) {
RETURN_IF_SKIPPED;
- for (auto config : contentConfigurations) {
- for (auto key : config.keys) {
+ for (const auto& config : contentConfigurations) {
+ for (const auto& key : config.keys) {
vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
const Pattern noPattern = {0, 0};
const vector<SubSample> subSamples = {{.numBytesOfClearData = 256,
diff --git a/drm/1.1/Android.bp b/drm/1.1/Android.bp
index dba3e427a..739b47078 100644
--- a/drm/1.1/Android.bp
+++ b/drm/1.1/Android.bp
@@ -16,13 +16,6 @@ hidl_interface {
"android.hardware.drm@1.0",
"android.hidl.base@1.0",
],
- types: [
- "DrmMetricGroup",
- "HdcpLevel",
- "KeyRequestType",
- "SecureStopRelease",
- "SecurityLevel",
- ],
gen_java: false,
}
diff --git a/drm/1.1/README.md b/drm/1.1/README.md
new file mode 100644
index 000000000..e88e6abce
--- /dev/null
+++ b/drm/1.1/README.md
@@ -0,0 +1,84 @@
+# VINTF Device Manifest
+
+In Android Pie, an `<fqname>` tag was introduced to be able to express multiple
+different versions of the same HAL in VINTF manifests (for DRM)
+in device manifest. For devices launching with previous versions of Android and
+upgrading to Android Pie, the device manifest must not use `<fqname>` to
+satisfy requirements for non-optional HALs, because older version of `libvintf`
+do not recognize it, causing errors during OTA update.
+
+Assuming that the HAL provides `@1.0::I*/default`,
+`@1.1::I*/clearkey` and `@1.1::I*/foo` instances:
+
+## Devices upgrading to Android Pie
+
+### `target-level=1` or `target-level=2`
+
+FCM (framework compatibility matrix) version 2 (released in Android Oreo MR1)
+requires DRM 1.0. If the new device manifest has Target FCM Version (i.e.
+`target-level`) 1 or 2, it should use the following snippet:
+
+```xml
+<hal format="hidl">
+ <name>android.hardware.drm</name>
+ <transport>hwbinder</transport>
+ <version>1.0</version>
+ <interface>
+ <name>ICryptoFactory</name>
+ <instance>default</instance>
+ </interface>
+ <interface>
+ <name>IDrmFactory</name>
+ <instance>default</instance>
+ </interface>
+ <fqname>@1.1::ICryptoFactory/clearkey</fqname>
+ <fqname>@1.1::IDrmFactory/clearkey</fqname>
+ <fqname>@1.1::ICryptoFactory/foo</fqname>
+ <fqname>@1.1::IDrmFactory/foo</fqname>
+</hal>
+```
+
+### `target-level=3`
+
+FCM (framework compatibility matrix) version 3 (released in Android Pie)
+requires DRM 1.1. If the new device manifest has Target FCM Version (i.e.
+`target-level`) 3, it should use the following snippet:
+
+
+```xml
+<hal format="hidl">
+ <name>android.hardware.drm</name>
+ <transport>hwbinder</transport>
+ <version>1.1</version>
+ <interface>
+ <name>ICryptoFactory</name>
+ <instance>clearkey</instance>
+ <instance>foo</instance>
+ </interface>
+ <interface>
+ <name>IDrmFactory</name>
+ <instance>clearkey</instance>
+ <instance>foo</instance>
+ </interface>
+ <fqname>@1.0::ICryptoFactory/default</fqname>
+ <fqname>@1.0::IDrmFactory/default</fqname>
+</hal>
+```
+
+## Devices launching with Android Pie
+If you have a new device launched with Android Pie (no OTA), both of the
+aforementioned snippets can be used. Besides, it is recommended to use the
+new, clearer format:
+
+```xml
+<hal format="hidl">
+ <name>android.hardware.drm</name>
+ <transport>hwbinder</transport>
+ <fqname>@1.0::ICryptoFactory/default</fqname>
+ <fqname>@1.0::IDrmFactory/default</fqname>
+ <fqname>@1.1::ICryptoFactory/clearkey</fqname>
+ <fqname>@1.1::IDrmFactory/clearkey</fqname>
+ <fqname>@1.1::ICryptoFactory/foo</fqname>
+ <fqname>@1.1::IDrmFactory/foo</fqname>
+</hal>
+```
diff --git a/drm/1.1/vts/functional/Android.bp b/drm/1.1/vts/functional/Android.bp
index 782f2833c..1090b6900 100644
--- a/drm/1.1/vts/functional/Android.bp
+++ b/drm/1.1/vts/functional/Android.bp
@@ -31,4 +31,5 @@ cc_test {
"libssl",
"libcrypto",
],
+ test_suites: ["general-tests"],
}
diff --git a/drm/1.1/vts/functional/drm_hal_clearkey_test.cpp b/drm/1.1/vts/functional/drm_hal_clearkey_test.cpp
index 124661696..6be30d34d 100644
--- a/drm/1.1/vts/functional/drm_hal_clearkey_test.cpp
+++ b/drm/1.1/vts/functional/drm_hal_clearkey_test.cpp
@@ -24,7 +24,7 @@
#include <android/hardware/drm/1.0/types.h>
#include <android/hardware/drm/1.1/types.h>
#include <android/hidl/allocator/1.0/IAllocator.h>
-#include <android/hidl/manager/1.0/IServiceManager.h>
+#include <android/hidl/manager/1.2/IServiceManager.h>
#include <gtest/gtest.h>
#include <hidl/HidlSupport.h>
#include <hidl/ServiceManagement.h>
@@ -129,9 +129,9 @@ public:
ALOGD("DrmHalClearkeyTest: Running test %s.%s", test_info->test_case_name(),
test_info->name());
- auto manager = android::hardware::defaultServiceManager();
+ auto manager = android::hardware::defaultServiceManager1_2();
ASSERT_NE(nullptr, manager.get());
- manager->listByInterface(IDrmFactory::descriptor,
+ manager->listManifestByInterface(IDrmFactory::descriptor,
[&](const hidl_vec<hidl_string> &registered) {
for (const auto &instance : registered) {
sp<IDrmFactory> drmFactory =
@@ -144,7 +144,7 @@ public:
}
);
- manager->listByInterface(ICryptoFactory::descriptor,
+ manager->listManifestByInterface(ICryptoFactory::descriptor,
[&](const hidl_vec<hidl_string> &registered) {
for (const auto &instance : registered) {
sp<ICryptoFactory> cryptoFactory =
@@ -228,13 +228,13 @@ protected:
const std::string& componentName, const VT& componentValue) {
bool validAttribute = false;
bool validComponent = false;
- for (DrmMetricGroup::Attribute attribute : metric.attributes) {
+ for (const DrmMetricGroup::Attribute& attribute : metric.attributes) {
if (attribute.name == attributeName &&
ValueEquals(attribute.type, attributeValue, attribute)) {
validAttribute = true;
}
}
- for (DrmMetricGroup::Value value : metric.values) {
+ for (const DrmMetricGroup::Value& value : metric.values) {
if (value.componentName == componentName &&
ValueEquals(value.type, componentValue, value)) {
validComponent = true;
diff --git a/drm/1.2/Android.bp b/drm/1.2/Android.bp
new file mode 100644
index 000000000..2d54302a5
--- /dev/null
+++ b/drm/1.2/Android.bp
@@ -0,0 +1,24 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+ name: "android.hardware.drm@1.2",
+ root: "android.hardware",
+ vndk: {
+ enabled: true,
+ },
+ srcs: [
+ "types.hal",
+ "ICryptoFactory.hal",
+ "ICryptoPlugin.hal",
+ "IDrmFactory.hal",
+ "IDrmPlugin.hal",
+ "IDrmPluginListener.hal",
+ ],
+ interfaces: [
+ "android.hardware.drm@1.0",
+ "android.hardware.drm@1.1",
+ "android.hidl.base@1.0",
+ ],
+ gen_java: false,
+}
+
diff --git a/drm/1.2/ICryptoFactory.hal b/drm/1.2/ICryptoFactory.hal
new file mode 100644
index 000000000..c4a9b4b31
--- /dev/null
+++ b/drm/1.2/ICryptoFactory.hal
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.drm@1.2;
+
+import @1.1::ICryptoFactory;
+
+/**
+ * ICryptoFactory is the main entry point for interacting with a vendor's
+ * crypto HAL to create crypto plugins. Crypto plugins create crypto sessions
+ * which are used by a codec to decrypt protected video content.
+ *
+ * The 1.2 factory must always create 1.2 ICryptoPlugin interfaces, which are
+ * returned via the 1.0 createPlugin method.
+ *
+ * To use 1.2 features the caller must cast the returned interface to a
+ * 1.2 HAL, using V1_2::IDrmPlugin::castFrom().
+ *
+ * The ICryptoFactory hal is required because all top-level interfaces
+ * have to be updated in a minor uprev.
+ */
+interface ICryptoFactory extends @1.1::ICryptoFactory {
+};
diff --git a/drm/1.2/ICryptoPlugin.hal b/drm/1.2/ICryptoPlugin.hal
new file mode 100644
index 000000000..07006768c
--- /dev/null
+++ b/drm/1.2/ICryptoPlugin.hal
@@ -0,0 +1,84 @@
+/**
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.drm@1.2;
+
+import @1.0::DestinationBuffer;
+import @1.0::ICryptoPlugin;
+import @1.0::Mode;
+import @1.0::Pattern;
+import @1.0::SessionId;
+import @1.0::SharedBuffer;
+import @1.0::SubSample;
+
+/**
+ * ICryptoPlugin is the HAL for vendor-provided crypto plugins.
+ * It allows crypto sessions to be opened and operated on, to
+ * load crypto keys for a codec to decrypt protected video content.
+ */
+interface ICryptoPlugin extends @1.0::ICryptoPlugin {
+
+ /**
+ * Decrypt an array of subsamples from the source memory buffer to the
+ * destination memory buffer.
+ *
+ * decrypt_1_2() only differs from decrypt() in that additional status
+ * codes must be returned.
+ *
+ * @param secure a flag to indicate if a secure decoder is being used. This
+ * enables the plugin to configure buffer modes to work consistently with
+ * a secure decoder.
+ * @param the keyId for the key that is used to do the the decryption. The
+ * keyId refers to a key in the associated MediaDrm instance.
+ * @param iv the initialization vector to use
+ * @param mode the crypto mode to use
+ * @param pattern the crypto pattern to use
+ * @param subSamples a vector of subsamples indicating the number
+ * of clear and encrypted bytes to process. This allows the decrypt
+ * call to operate on a range of subsamples in a single call
+ * @param source the input buffer for the decryption
+ * @param offset the offset of the first byte of encrypted data from
+ * the base of the source buffer
+ * @param destination the output buffer for the decryption
+ * @return status the status of the call. The status must be OK or one
+ * of the following errors:
+ * ERROR_DRM_NO_LICENSE if no license keys have been loaded
+ * ERROR_DRM_LICENSE_EXPIRED if the license keys have expired
+ * ERROR_DRM_RESOURCE_BUSY if the resources required to perform
+ * the decryption are not available
+ * ERROR_DRM_INSUFFICIENT_OUTPUT_PROTECTION if required output
+ * protections are not active
+ * ERROR_DRM_INSUFFICIENT_SECURITY if the security level of the
+ * device is not sufficient to meet the requirements in
+ * the license policy
+ * ERROR_DRM_FRAME_TOO_LARGE if the frame being decrypted into
+ * the secure output buffer exceeds the size of the buffer
+ * ERROR_DRM_SESSION_NOT_OPENED if the decrypt session is not
+ * opened
+ * ERROR_DRM_DECRYPT if the decrypt operation fails
+ * ERROR_DRM_INVALID_STATE if the device is in a state where it
+ * is not able to perform decryption
+ * ERROR_DRM_CANNOT_HANDLE in other failure cases.
+ *
+ * @return bytesWritten the number of bytes output from the decryption
+ * @return detailedError if the error is a vendor-specific error, the
+ * vendor's crypto HAL may provide a detailed error string to help
+ * describe the error.
+ */
+ decrypt_1_2(bool secure, uint8_t[16] keyId, uint8_t[16] iv, Mode mode,
+ Pattern pattern, vec<SubSample> subSamples,
+ SharedBuffer source, uint64_t offset, DestinationBuffer destination)
+ generates(Status status, uint32_t bytesWritten, string detailedError);
+};
diff --git a/drm/1.2/IDrmFactory.hal b/drm/1.2/IDrmFactory.hal
new file mode 100644
index 000000000..682889c22
--- /dev/null
+++ b/drm/1.2/IDrmFactory.hal
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.drm@1.2;
+
+import @1.1::IDrmFactory;
+import @1.1::IDrmPlugin;
+import @1.1::SecurityLevel;
+
+/**
+ * IDrmFactory is the main entry point for interacting with a vendor's
+ * drm HAL to create drm plugin instances. A drm plugin instance
+ * creates drm sessions which are used to obtain keys for a crypto
+ * session so it can decrypt protected video content.
+ *
+ * The 1.2 factory must always create 1.2 IDrmPlugin interfaces, which are
+ * returned via the 1.0 createPlugin method.
+ *
+ * To use 1.2 features the caller must cast the returned interface to a
+ * 1.2 HAL, using V1_2::IDrmPlugin::castFrom().
+ *
+ * The IDrmFactory hal is required because all top-level interfaces
+ * have to be updated in a minor uprev.
+ */
+
+interface IDrmFactory extends @1.1::IDrmFactory {
+ /**
+ * Determine if a specific security level is supported by the device.
+ * This method only differs from @1.0 isCryptoSchemeSupported
+ * by the addition of a security level.
+ *
+ * @param uuid identifies the crypto scheme in question
+ * @param mimeType identifies the mime type in question
+ * @param securityLevel specifies the security level required
+ * @return isSupported must be true only if the scheme is supported
+ */
+ isCryptoSchemeSupported_1_2(uint8_t[16] uuid, string mimeType,
+ @1.1::SecurityLevel securityLevel) generates(bool isSupported);
+};
diff --git a/drm/1.2/IDrmPlugin.hal b/drm/1.2/IDrmPlugin.hal
new file mode 100644
index 000000000..df09ccf91
--- /dev/null
+++ b/drm/1.2/IDrmPlugin.hal
@@ -0,0 +1,247 @@
+/**
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.drm@1.2;
+
+import @1.0::KeyedVector;
+import @1.0::KeyType;
+import @1.0::SessionId;
+import @1.0::Status;
+import @1.1::IDrmPlugin;
+import @1.1::KeyRequestType;
+import @1.2::IDrmPluginListener;
+
+/**
+ * IDrmPlugin is used to interact with a specific drm plugin that was
+ * created by IDrm::createPlugin. A drm plugin provides methods for
+ * obtaining drm keys to be used by a codec to decrypt protected video
+ * content.
+ */
+interface IDrmPlugin extends @1.1::IDrmPlugin {
+
+ /**
+ * The keys in an offline license allow protected content to be
+ * played even if the device is not connected to a network.
+ * Offline licenses are stored on the device after a key
+ * request/response exchange when the key request KeyType is
+ * OFFLINE. Normally each app is responsible for keeping track of
+ * the KeySetIds it has created. In some situations however, it
+ * will be necessary to request the list of stored offline license
+ * KeySetIds. If an app loses the KeySetId for any stored licenses
+ * that it created, for example, it must be able to recover the
+ * stored KeySetIds so those licenses will be removed when they
+ * expire or when the app is uninstalled.
+ * <p>
+ * This method returns a list of the KeySetIds for all offline
+ * licenses. The offline license KeySetId allows an app to query
+ * the status of an offline license or remove it.
+ *
+ * @return status the status of the call. Must be OK or
+ * ERROR_DRM_INVALID_STATE if the HAL is in a state where the
+ * KeySetIds can't be returned.
+ * @return a list of offline license keySetIds. If there are no offline
+ * licenses, the list must be empty and OK must be returned as the
+ * status.
+ */
+ getOfflineLicenseKeySetIds() generates (@1.0::Status status,
+ vec<KeySetId> keySetIds);
+
+ /**
+ * Normally offline licenses are released using a key
+ * request/response exchange using getKeyRequest where the KeyType
+ * is RELEASE, followed by provideKeyResponse. This allows the
+ * server to cryptographically confirm that the license has been
+ * removed and then adjust the count of offline licenses allocated
+ * to the device.
+ * <p>
+ * In some exceptional situations it will be necessary to directly
+ * remove offline licenses without notifying the server, which is
+ * performed by this method.
+ *
+ * @param keySetId the id of the offline license to remove
+ * @return status the status of the call. Must be one of OK on
+ * success, BAD_VALUE if the license is not found or
+ * ERROR_DRM_INVALID_STATE if the HAL is in a state where the
+ * KeySetIds can't be removed.
+ */
+ removeOfflineLicense(KeySetId keySetId) generates (@1.0::Status status);
+
+ /**
+ * Request the state of an offline license. An offline license must
+ * be usable or inactive. The keys in a usable offline license are
+ * available for decryption. When the offline license state is
+ * inactive, the keys have been marked for release using
+ * getKeyRequest with KeyType RELEASE but the key response has not
+ * been received. The keys in an inactive offline license are not
+ * usable for decryption.
+ *
+ * @param keySetId the id of the offline license
+ * @return status the status of the call. Must be one of OK on
+ * success, BAD_VALUE if the license is not found or
+ * ERROR_DRM_INVALID_STATE if the HAL is in a state where the
+ * offline license state can't be queried.
+ * @return the offline license state, one of USABLE or INACTIVE.
+ * If the return status is not OK then state must be set to
+ * UNKNOWN.
+ */
+ getOfflineLicenseState(KeySetId keySetId) generates (
+ @1.0::Status status, OfflineLicenseState state);
+
+ /**
+ * A key request/response exchange occurs between the app and a License
+ * Server to obtain the keys required to decrypt the content.
+ * getKeyRequest_1_2() is used to obtain an opaque key request blob that is
+ * delivered to the license server.
+ *
+ * getKeyRequest_1_2() only differs from getKeyRequest_1_1() in that
+ * additional status codes must be returned.
+ *
+ * @param scope either a sessionId or a keySetId, depending on the
+ * specified keyType. When the keyType is OFFLINE or STREAMING, scope
+ * must be set to the sessionId the keys will be provided to. When the
+ * keyType is RELEASE, scope must be set to the keySetId of the keys
+ * being released.
+ * @param initData container-specific data, its meaning is interpreted
+ * based on the mime type provided in the mimeType parameter. It could
+ * contain, for example, the content ID, key ID or other data obtained
+ * from the content metadata that is required to generate the key
+ * request. initData must be empty when keyType is RELEASE.
+ * @param mimeType identifies the mime type of the content
+ * @param keyType specifies if the keys are to be used for streaming,
+ * offline or a release
+ * @param optionalParameters included in the key request message to
+ * allow a client application to provide additional message parameters
+ * to the server.
+ * @return status the status of the call. The status must be OK or one of
+ * the following errors: ERROR_DRM_SESSION_NOT_OPENED if the session is
+ * not opened, ERROR_DRM_NOT_PROVISIONED if the device requires
+ * provisioning before it is able to generate a key request,
+ * ERROR_DRM_RESOURCE_CONTENTION if client applications using the hal
+ * are temporarily exceeding the available crypto resources such that a
+ * retry of the operation is likely to succeed, ERROR_DRM_CANNOT_HANDLE
+ * if getKeyRequest is not supported at the time of the call, BAD_VALUE
+ * if any parameters are invalid or ERROR_DRM_INVALID_STATE if the HAL
+ * is in a state where a key request cannot be generated.
+ * @return request if successful, the opaque key request blob is returned
+ * @return requestType indicates type information about the returned
+ * request. The type must be one of INITIAL, RENEWAL, RELEASE, NONE or
+ * UPDATE. An INITIAL request is the first key request for a
+ * license. RENEWAL is a subsequent key request used to refresh the
+ * keys in a license. RELEASE corresponds to a keyType of RELEASE,
+ * which indicates keys are being released. NONE indicates that no
+ * request is needed because the keys are already loaded. UPDATE
+ * indicates that the keys need to be refetched after the initial
+ * license request.
+ * @return defaultUrl the URL that the request may be sent to, if
+ * provided by the drm HAL. The app can choose to override this URL.
+ */
+ getKeyRequest_1_2(vec<uint8_t> scope, vec<uint8_t> initData,
+ string mimeType, KeyType keyType, KeyedVector optionalParameters)
+ generates (Status status, vec<uint8_t> request,
+ KeyRequestType requestType, string defaultUrl);
+
+ /**
+ * A provision request/response exchange occurs between the app and a
+ * provisioning server to retrieve a device certificate. getProvisionRequest
+ * is used to obtain an opaque provisioning request blob that is delivered
+ * to the provisioning server.
+ *
+ * getProvisionRequest_1_2() only differs from getProvisionRequest_1_0() in
+ * that additional status codes must be returned.
+ *
+ * @param certificateType the type of certificate requested, e.g. "X.509"
+ * @param certificateAuthority identifies the certificate authority. A
+ * certificate authority (CA) is an entity which issues digital
+ * certificates for use by other parties. It is an example of a trusted
+ * third party.
+ * @return status the status of the call. The status must be OK or one of
+ * the following errors: ERROR_DRM_RESOURCE_CONTENTION if client
+ * applications using the hal are temporarily exceeding the available
+ * crypto resources such that a retry of the operation is likely to
+ * succeed, ERROR_DRM_CANNOT_HANDLE if the drm scheme does not require
+ * provisioning or ERROR_DRM_INVALID_STATE if the HAL is in a state
+ * where the provision request cannot be generated.
+ * @return request if successful the opaque certificate request blob
+ * is returned
+ * @return defaultUrl URL that the provisioning request may be
+ * sent to, if known by the HAL implementation. An app can choose to
+ * override this URL. If the HAL implementation does not provide a
+ * defaultUrl, the returned string must be empty.
+ */
+ getProvisionRequest_1_2(string certificateType, string certificateAuthority)
+ generates (Status status, vec<uint8_t> request, string defaultUrl);
+
+ /**
+ * Return the currently negotiated and max supported HDCP levels.
+ *
+ * This method only differs from @1.1 version by the addition of
+ * support for HDCP 2.3.
+ *
+ * The current level is based on the display(s) the device is connected to.
+ * If multiple HDCP-capable displays are simultaneously connected to
+ * separate interfaces, this method returns the lowest negotiated HDCP level
+ * of all interfaces.
+ *
+ * The maximum HDCP level is the highest level that can potentially be
+ * negotiated. It is a constant for any device, i.e. it does not depend on
+ * downstream receiving devices that could be connected. For example, if
+ * the device has HDCP 1.x keys and is capable of negotiating HDCP 1.x, but
+ * does not have HDCP 2.x keys, then the maximum HDCP capability would be
+ * reported as 1.x. If multiple HDCP-capable interfaces are present, it
+ * indicates the highest of the maximum HDCP levels of all interfaces.
+ *
+ * This method should only be used for informational purposes, not for
+ * enforcing compliance with HDCP requirements. Trusted enforcement of HDCP
+ * policies must be handled by the DRM system.
+ *
+ * @return status the status of the call. The status must be OK or
+ * ERROR_DRM_INVALID_STATE if the HAL is in a state where the HDCP
+ * level cannot be queried.
+ * @return connectedLevel the lowest HDCP level for any connected
+ * displays
+ * @return maxLevel the highest HDCP level that can be supported
+ * by the device
+ */
+ getHdcpLevels_1_2() generates (Status status, HdcpLevel connectedLevel,
+ HdcpLevel maxLevel);
+
+ /**
+ * Send a session lost state event to the listener. This event
+ * indicates that a session's state has become invalid because the
+ * device crypto hardware is incapable of retaining crypto session
+ * state across suspend and resume cycles.
+ *
+ * @param sessionId identifies the session the event originated from
+ */
+ sendSessionLostState(SessionId sessionId);
+
+ /**
+ * Send a keys change event to the listener. The keys change event
+ * indicates the status of each key in the session. Keys can be
+ * indicated as being usable, expired, outputnotallowed or statuspending.
+ *
+ * This method only differs from @1.0 version by the addition of new
+ * KeyStatusType(s) in keyStatusList.
+ *
+ * @param sessionId identifies the session the event originated from
+ * @param keyStatusList indicates the status for each key ID in the
+ * session.
+ * @param hasNewUsableKey indicates if the event includes at least one
+ * key that has become usable.
+ */
+ sendKeysChange_1_2(SessionId sessionId, vec<KeyStatus> keyStatusList,
+ bool hasNewUsableKey);
+
+};
diff --git a/drm/1.2/IDrmPluginListener.hal b/drm/1.2/IDrmPluginListener.hal
new file mode 100644
index 000000000..e8cb91a9c
--- /dev/null
+++ b/drm/1.2/IDrmPluginListener.hal
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.drm@1.2;
+
+import @1.0::IDrmPluginListener;
+import @1.0::SessionId;
+
+/**
+ * IDrmPluginListener is a listener interface for Drm events sent from an
+ * IDrmPlugin instance.
+ */
+interface IDrmPluginListener extends @1.0::IDrmPluginListener {
+ /**
+ * Some device crypto hardware is incapable of retaining crypto
+ * session state across suspend and resume cycles. A
+ * SessionLostState event must be signaled when a session has
+ * become invalid for this reason. This event must not be used to
+ * indicate a failure in the crypto system. Closing the session
+ * and opening a new one must allow the application to resume
+ * normal use of the drm hal module.
+ *
+ * @param sessionId identifies the session that has been invalidated
+ */
+ oneway sendSessionLostState(SessionId sessionId);
+
+ /**
+ * Send a keys change event to the listener. The keys change event
+ * indicates the status of each key in the session. Keys can be
+ * indicated as being usable, expired, outputnotallowed or statuspending.
+ *
+ * This method only differs from @1.0 version by the addition of new
+ * KeyStatusType(s) in keyStatusList.
+ *
+ * @param sessionId identifies the session the event originated from
+ * @param keyStatusList indicates the status for each key ID in the
+ * session.
+ * @param hasNewUsableKey indicates if the event includes at least one
+ * key that has become usable.
+ */
+ oneway sendKeysChange_1_2(SessionId sessionId, vec<KeyStatus> keyStatusList,
+ bool hasNewUsableKey);
+
+};
diff --git a/drm/1.2/types.hal b/drm/1.2/types.hal
new file mode 100644
index 000000000..87218a420
--- /dev/null
+++ b/drm/1.2/types.hal
@@ -0,0 +1,118 @@
+/**
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.drm@1.2;
+
+import @1.0::KeyStatusType;
+import @1.0::Status;
+import @1.1::HdcpLevel;
+
+enum OfflineLicenseState : uint32_t {
+ /**
+ * Offline license state is unknown
+ */
+ UNKNOWN,
+
+ /**
+ * Offline license state is usable, the keys are usable for decryption.
+ */
+ USABLE,
+
+ /**
+ * Offline license state is inactive, the keys have been marked for
+ * release using {@link #getKeyRequest} with KEY_TYPE_RELEASE but the
+ * key response has not been received.
+ */
+ INACTIVE
+};
+
+enum Status : @1.0::Status {
+ /**
+ * The drm HAL module must return ERROR_DRM_INSUFFICIENT_SECURITY
+ * from the crypto plugin decrypt method when the security level
+ * of the device is not sufficient to meet the requirements in the
+ * license policy.
+ */
+ ERROR_DRM_INSUFFICIENT_SECURITY,
+
+ /**
+ * The drm HAL module must return ERROR_FRAME_TOO_LARGE from the
+ * decrypt method when the frame being decrypted into the secure
+ * output buffer exceeds the size of the buffer.
+ */
+ ERROR_DRM_FRAME_TOO_LARGE,
+
+ /**
+ * This error must be returned from any session method when an
+ * attempt is made to use the session after the crypto hardware
+ * state has been invalidated. Some devices are not able to
+ * retain crypto session state across device suspend/resume which
+ * results in invalid session state.
+ */
+ ERROR_DRM_SESSION_LOST_STATE,
+
+ /**
+ * The drm HAL module must return this error if client
+ * applications using the hal are temporarily exceeding the
+ * capacity of available crypto resources such that a retry of
+ * the operation is likely to succeed.
+ */
+ ERROR_DRM_RESOURCE_CONTENTION,
+};
+
+/**
+ * HDCP specifications are defined by Digital Content Protection LLC (DCP).
+ * "HDCP Specification Rev. 2.3 Interface Independent Adaptation"
+ * "HDCP 2.3 on HDMI Specification"
+ */
+enum HdcpLevel : @1.1::HdcpLevel {
+ /**
+ * HDCP version 2.3 Type 1.
+ */
+ HDCP_V2_3
+};
+
+
+/**
+ * KeySetId is an identifier that references a set of keys in an
+ * offline license. The keySetId is created by the HAL implementation
+ * and returned from provideKeyResponse and getOfflineLicenseIds. The
+ * framework passes KeySetId back to the HAL when referring to the key
+ * set in methods that take a KeySetId as an input parameter.
+ */
+typedef vec<uint8_t> KeySetId;
+
+enum KeyStatusType : @1.0::KeyStatusType {
+ /**
+ * The key is not yet usable to decrypt media because the start
+ * time is in the future. The key must become usable when
+ * its start time is reached.
+ */
+ USABLEINFUTURE
+};
+
+/**
+ * Used by sendKeysChange_1_2 to report the usability status of each key to the
+ * app.
+ *
+ * This struct only differs from @1.0 version by the addition of new
+ * KeyStatusType(s).
+ *
+ */
+struct KeyStatus {
+ KeySetId keyId;
+ KeyStatusType type;
+};
diff --git a/drm/1.2/vts/functional/Android.bp b/drm/1.2/vts/functional/Android.bp
new file mode 100644
index 000000000..6b4a4c0ad
--- /dev/null
+++ b/drm/1.2/vts/functional/Android.bp
@@ -0,0 +1,40 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_test {
+ name: "VtsHalDrmV1_2TargetTest",
+ defaults: ["VtsHalTargetTestDefaults"],
+ srcs: [
+ "drm_hal_clearkey_module.cpp",
+ "drm_hal_common.cpp",
+ "drm_hal_test.cpp",
+ "vendor_modules.cpp",
+ ],
+ include_dirs: ["hardware/interfaces/drm/1.0/vts/functional"],
+ static_libs: [
+ "android.hardware.drm@1.0",
+ "android.hardware.drm@1.1",
+ "android.hardware.drm@1.2",
+ "android.hardware.drm@1.0-helper",
+ "android.hidl.allocator@1.0",
+ "android.hidl.memory@1.0",
+ "libhidlmemory",
+ "libnativehelper",
+ "libssl",
+ "libcrypto",
+ ],
+ test_suites: ["general-tests"],
+}
diff --git a/drm/1.2/vts/functional/drm_hal_clearkey_module.cpp b/drm/1.2/vts/functional/drm_hal_clearkey_module.cpp
new file mode 100644
index 000000000..a0dc00155
--- /dev/null
+++ b/drm/1.2/vts/functional/drm_hal_clearkey_module.cpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "drm_hal_clearkey_module@1.2"
+
+#include <gtest/gtest.h>
+#include "drm_hal_clearkey_module.h"
+
+namespace android {
+namespace hardware {
+namespace drm {
+namespace V1_2 {
+namespace vts {
+
+std::vector<uint8_t> DrmHalVTSClearkeyModule::handleProvisioningRequest(
+ const std::vector<uint8_t>& /*provisioningRequest*/,
+ const std::string& /*url*/) {
+ EXPECT_TRUE(false) << "Clearkey doesn't support provisioning";
+ return {};
+}
+
+std::vector<DrmHalVTSClearkeyModule::ContentConfiguration>
+ DrmHalVTSClearkeyModule::getContentConfigurations() const {
+ DrmHalVTSClearkeyModule::ContentConfiguration conf = {
+ .name = "DrmHalVTSClearkeyModule", // name
+ .serverUrl = "", // serverUrl
+ .initData = { // initData
+ // BMFF box header (4 bytes size + 'pssh')
+ 0x00, 0x00, 0x00, 0x34, 0x70, 0x73, 0x73, 0x68,
+ // full box header (version = 1 flags = 0)
+ 0x01, 0x00, 0x00, 0x00,
+ // system id
+ 0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, 0xac, 0xe3, 0x3c,
+ 0x1e, 0x52, 0xe2, 0xfb, 0x4b,
+ // number of key ids
+ 0x00, 0x00, 0x00, 0x01,
+ // key id
+ 0x60, 0x06, 0x1e, 0x01, 0x7e, 0x47, 0x7e, 0x87, 0x7e, 0x57, 0xd0,
+ 0x0d, 0x1e, 0xd0, 0x0d, 0x1e,
+ // size of data, must be zero
+ 0x00, 0x00, 0x00, 0x00
+ },
+ .mimeType = "video/mp4", // mimeType
+ .optionalParameters = {}, // optionalParameters
+ .policy = { .allowOffline = true }, // allowOffline
+ .keys = { // keys
+ {
+ .isSecure = false, // isSecure
+ .keyId = { // keyId
+ 0x60, 0x06, 0x1e, 0x01, 0x7e, 0x47, 0x7e, 0x87,
+ 0x7e, 0x57, 0xd0, 0x0d, 0x1e, 0xd0, 0x0d, 0x1e
+ },
+ .clearContentKey = { // clearContentKey
+ 0x1a, 0x8a, 0x20, 0x95, 0xe4, 0xde, 0xb2, 0xd2,
+ 0x9e, 0xc8, 0x16, 0xac, 0x7b, 0xae, 0x20, 0x82
+ }
+ }
+ }
+ };
+ return { conf };
+}
+
+std::vector<uint8_t> DrmHalVTSClearkeyModule::handleKeyRequest(
+ const std::vector<uint8_t>& keyRequest,
+ const std::string& /*serverUrl*/) {
+
+ // {"kids":["YAYeAX5Hfod-V9ANHtANHg"],"type":"temporary"}
+ std::vector<uint8_t> expectedKeyRequest = {
+ 0x7b, 0x22, 0x6b, 0x69, 0x64, 0x73, 0x22, 0x3a, 0x5b, 0x22, 0x59, 0x41, 0x59, 0x65,
+ 0x41, 0x58, 0x35, 0x48, 0x66, 0x6f, 0x64, 0x2d, 0x56, 0x39, 0x41, 0x4e, 0x48, 0x74,
+ 0x41, 0x4e, 0x48, 0x67, 0x22, 0x5d, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a,
+ 0x22, 0x74, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x72, 0x79, 0x22, 0x7d};
+
+ // {"kids":["YAYeAX5Hfod-V9ANHtANHg"],"type":"persistent-license"}
+ std::vector<uint8_t> expectedKeyRequestPersistent = {
+ 0x7b, 0x22, 0x6b, 0x69, 0x64, 0x73, 0x22, 0x3a, 0x5b, 0x22, 0x59, 0x41, 0x59, 0x65,
+ 0x41, 0x58, 0x35, 0x48, 0x66, 0x6f, 0x64, 0x2d, 0x56, 0x39, 0x41, 0x4e, 0x48, 0x74,
+ 0x41, 0x4e, 0x48, 0x67, 0x22, 0x5d, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a,
+ 0x22, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x69,
+ 0x63, 0x65, 0x6e, 0x73, 0x65, 0x22, 0x7d};
+
+ // {"keys":[{"kty":"oct","kid":"YAYeAX5Hfod-V9ANHtANHg","k":"GoogleTestKeyBase64ggg"}]}
+ std::vector<uint8_t> knownKeyResponse = {
+ 0x7b, 0x22, 0x6b, 0x65, 0x79, 0x73, 0x22, 0x3a, 0x5b, 0x7b, 0x22, 0x6b, 0x74, 0x79, 0x22,
+ 0x3a, 0x22, 0x6f, 0x63, 0x74, 0x22, 0x2c, 0x22, 0x6b, 0x69, 0x64, 0x22, 0x3a, 0x22, 0x59,
+ 0x41, 0x59, 0x65, 0x41, 0x58, 0x35, 0x48, 0x66, 0x6f, 0x64, 0x2d, 0x56, 0x39, 0x41, 0x4e,
+ 0x48, 0x74, 0x41, 0x4e, 0x48, 0x67, 0x22, 0x2c, 0x22, 0x6b, 0x22, 0x3a, 0x22, 0x47, 0x6f,
+ 0x6f, 0x67, 0x6c, 0x65, 0x54, 0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x42, 0x61, 0x73, 0x65,
+ 0x36, 0x34, 0x67, 0x67, 0x67, 0x22, 0x7d, 0x5d, 0x7d};
+
+ // {"keys":[{"kty":"oct","kid":"YAYeAX5Hfod-V9ANHtANHg","k":"GoogleTestKeyBase64ggg"}],"type":"persistent-license"}
+ std::vector<uint8_t> knownKeyResponsePersistent = {
+ 0x7b, 0x22, 0x6b, 0x65, 0x79, 0x73, 0x22, 0x3a, 0x5b, 0x7b, 0x22, 0x6b, 0x74, 0x79, 0x22,
+ 0x3a, 0x22, 0x6f, 0x63, 0x74, 0x22, 0x2c, 0x22, 0x6b, 0x69, 0x64, 0x22, 0x3a, 0x22, 0x59,
+ 0x41, 0x59, 0x65, 0x41, 0x58, 0x35, 0x48, 0x66, 0x6f, 0x64, 0x2d, 0x56, 0x39, 0x41, 0x4e,
+ 0x48, 0x74, 0x41, 0x4e, 0x48, 0x67, 0x22, 0x2c, 0x22, 0x6b, 0x22, 0x3a, 0x22, 0x47, 0x6f,
+ 0x6f, 0x67, 0x6c, 0x65, 0x54, 0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x42, 0x61, 0x73, 0x65,
+ 0x36, 0x34, 0x67, 0x67, 0x67, 0x22, 0x7d, 0x5d, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22,
+ 0x3a, 0x22, 0x70, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x69,
+ 0x63, 0x65, 0x6e, 0x73, 0x65, 0x22, 0x7d};
+
+ std::string req(keyRequest.begin(), keyRequest.end());
+ if (req.find("persistent-license") != std::string::npos) {
+ EXPECT_EQ(expectedKeyRequestPersistent, keyRequest);
+ return knownKeyResponsePersistent;
+ } else {
+ EXPECT_EQ(expectedKeyRequest, keyRequest);
+ return knownKeyResponse;
+ }
+
+}
+
+} // namespace vts
+} // namespace V1_2
+} // namespace drm
+} // namespace hardware
+} // namespace android
diff --git a/drm/1.2/vts/functional/drm_hal_clearkey_module.h b/drm/1.2/vts/functional/drm_hal_clearkey_module.h
new file mode 100644
index 000000000..7250cf25d
--- /dev/null
+++ b/drm/1.2/vts/functional/drm_hal_clearkey_module.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DRM_HAL_CLEARKEY_MODULE_H
+#define DRM_HAL_CLEARKEY_MODULE_H
+
+#include "drm_hal_vendor_module_api.h"
+
+namespace android {
+namespace hardware {
+namespace drm {
+namespace V1_2 {
+namespace vts {
+
+class DrmHalVTSClearkeyModule : public DrmHalVTSVendorModule_V1 {
+ public:
+ DrmHalVTSClearkeyModule() {}
+ virtual ~DrmHalVTSClearkeyModule() {}
+
+ virtual uint32_t getAPIVersion() const override { return 1; }
+
+ virtual std::string getServiceName() const override { return "clearkey"; }
+
+ virtual std::vector<uint8_t> getUUID() const override {
+ return {0xE2, 0x71, 0x9D, 0x58, 0xA9, 0x85, 0xB3, 0xC9,
+ 0x78, 0x1A, 0xB0, 0x30, 0xAF, 0x78, 0xD3, 0x0E };
+ }
+
+
+ virtual std::vector<uint8_t> handleProvisioningRequest(
+ const std::vector<uint8_t>& provisioningRequest,
+ const std::string& url) override;
+
+ virtual std::vector<DrmHalVTSClearkeyModule::ContentConfiguration>
+ getContentConfigurations() const override;
+
+ virtual std::vector<uint8_t> handleKeyRequest(
+ const std::vector<uint8_t>& keyRequest,
+ const std::string& serverUrl) override;
+};
+
+} // namespace vts
+} // namespace V1_2
+} // namespace drm
+} // namespace hardware
+} // namespace android
+
+#endif // DRM_HAL_CLEARKEY_MODULE_H
diff --git a/drm/1.2/vts/functional/drm_hal_common.cpp b/drm/1.2/vts/functional/drm_hal_common.cpp
new file mode 100644
index 000000000..bfffbe8d2
--- /dev/null
+++ b/drm/1.2/vts/functional/drm_hal_common.cpp
@@ -0,0 +1,496 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "drm_hal_common@1.2"
+
+#include <android/hidl/allocator/1.0/IAllocator.h>
+#include <gtest/gtest.h>
+#include <hidl/HidlSupport.h>
+#include <hidlmemory/mapping.h>
+#include <log/log.h>
+#include <openssl/aes.h>
+#include <random>
+
+#include "drm_hal_clearkey_module.h"
+#include "drm_hal_common.h"
+
+using ::android::hardware::drm::V1_0::BufferType;
+using ::android::hardware::drm::V1_0::DestinationBuffer;
+using ICryptoPluginV1_0 = ::android::hardware::drm::V1_0::ICryptoPlugin;
+using IDrmPluginV1_0 = ::android::hardware::drm::V1_0::IDrmPlugin;
+using ::android::hardware::drm::V1_0::KeyValue;
+using ::android::hardware::drm::V1_0::SharedBuffer;
+using StatusV1_0 = ::android::hardware::drm::V1_0::Status;
+
+using ::android::hardware::drm::V1_1::KeyRequestType;
+
+using ::android::hardware::drm::V1_2::KeySetId;
+using ::android::hardware::drm::V1_2::OfflineLicenseState;
+using StatusV1_2 = ::android::hardware::drm::V1_2::Status;
+
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_memory;
+
+using ::android::hidl::allocator::V1_0::IAllocator;
+
+using std::random_device;
+using std::mt19937;
+
+namespace android {
+namespace hardware {
+namespace drm {
+namespace V1_2 {
+namespace vts {
+
+const char *kCallbackLostState = "LostState";
+const char *kCallbackKeysChange = "KeysChange";
+
+drm_vts::VendorModules *DrmHalTest::gVendorModules = nullptr;
+
+/**
+ * DrmHalPluginListener
+ */
+
+Return<void> DrmHalPluginListener::sendSessionLostState(const hidl_vec<uint8_t>& sessionId) {
+ ListenerEventArgs args;
+ args.sessionId = sessionId;
+ NotifyFromCallback(kCallbackLostState, args);
+ return Void();
+}
+
+Return<void> DrmHalPluginListener::sendKeysChange_1_2(const hidl_vec<uint8_t>& sessionId,
+ const hidl_vec<KeyStatus>& keyStatusList, bool hasNewUsableKey) {
+ ListenerEventArgs args;
+ args.sessionId = sessionId;
+ args.keyStatusList = keyStatusList;
+ args.hasNewUsableKey = hasNewUsableKey;
+ NotifyFromCallback(kCallbackKeysChange, args);
+ return Void();
+}
+
+/**
+ * DrmHalTest
+ */
+
+DrmHalTest::DrmHalTest()
+ : vendorModule(GetParam() == "clearkey"
+ ? new DrmHalVTSClearkeyModule()
+ : static_cast<DrmHalVTSVendorModule_V1*>(gVendorModules->getModule(GetParam()))),
+ contentConfigurations(vendorModule->getContentConfigurations()) {
+}
+
+void DrmHalTest::SetUp() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+
+ ALOGD("Running test %s.%s from (vendor) module %s",
+ test_info->test_case_name(), test_info->name(),
+ GetParam().c_str());
+
+ string name = vendorModule->getServiceName();
+ drmFactory = VtsHalHidlTargetTestBase::getService<IDrmFactory>(name);
+ if (drmFactory == nullptr) {
+ drmFactory = VtsHalHidlTargetTestBase::getService<IDrmFactory>();
+ }
+ if (drmFactory != nullptr) {
+ drmPlugin = createDrmPlugin();
+ }
+
+ cryptoFactory = VtsHalHidlTargetTestBase::getService<ICryptoFactory>(name);
+ if (cryptoFactory == nullptr) {
+ cryptoFactory = VtsHalHidlTargetTestBase::getService<ICryptoFactory>();
+ }
+ if (cryptoFactory != nullptr) {
+ cryptoPlugin = createCryptoPlugin();
+ }
+
+ // If drm scheme not installed skip subsequent tests
+ if (!drmFactory->isCryptoSchemeSupported(getVendorUUID())) {
+ vendorModule->setInstalled(false);
+ return;
+ }
+
+ ASSERT_NE(nullptr, drmPlugin.get()) << "Can't find " << vendorModule->getServiceName() << " drm@1.2 plugin";
+ ASSERT_NE(nullptr, cryptoPlugin.get()) << "Can't find " << vendorModule->getServiceName() << " crypto@1.2 plugin";
+
+}
+
+sp<IDrmPlugin> DrmHalTest::createDrmPlugin() {
+ if (drmFactory == nullptr) {
+ return nullptr;
+ }
+ sp<IDrmPlugin> plugin = nullptr;
+ hidl_string packageName("android.hardware.drm.test");
+ auto res = drmFactory->createPlugin(
+ getVendorUUID(), packageName,
+ [&](StatusV1_0 status, const sp<IDrmPluginV1_0>& pluginV1_0) {
+ EXPECT_EQ(StatusV1_0::OK, status);
+ plugin = IDrmPlugin::castFrom(pluginV1_0);
+ });
+
+ if (!res.isOk()) {
+ ALOGE("createDrmPlugin remote call failed");
+ }
+ return plugin;
+}
+
+sp<ICryptoPlugin> DrmHalTest::createCryptoPlugin() {
+ if (cryptoFactory == nullptr) {
+ return nullptr;
+ }
+ sp<ICryptoPlugin> plugin = nullptr;
+ hidl_vec<uint8_t> initVec;
+ auto res = cryptoFactory->createPlugin(
+ getVendorUUID(), initVec,
+ [&](StatusV1_0 status, const sp<ICryptoPluginV1_0>& pluginV1_0) {
+ EXPECT_EQ(StatusV1_0::OK, status);
+ plugin = ICryptoPlugin::castFrom(pluginV1_0);
+ });
+ if (!res.isOk()) {
+ ALOGE("createCryptoPlugin remote call failed");
+ }
+ return plugin;
+}
+
+hidl_array<uint8_t, 16> DrmHalTest::getVendorUUID() {
+ vector<uint8_t> uuid = vendorModule->getUUID();
+ return hidl_array<uint8_t, 16>(&uuid[0]);
+}
+
+/**
+ * Helper method to open a session and verify that a non-empty
+ * session ID is returned
+ */
+SessionId DrmHalTest::openSession() {
+ SessionId sessionId;
+
+ auto res = drmPlugin->openSession([&](StatusV1_0 status, const hidl_vec<unsigned char> &id) {
+ EXPECT_EQ(StatusV1_0::OK, status);
+ EXPECT_NE(id.size(), 0u);
+ sessionId = id;
+ });
+ EXPECT_OK(res);
+ return sessionId;
+}
+
+/**
+ * Helper method to close a session
+ */
+void DrmHalTest::closeSession(const SessionId& sessionId) {
+ StatusV1_0 status = drmPlugin->closeSession(sessionId);
+ EXPECT_EQ(StatusV1_0::OK, status);
+}
+
+hidl_vec<uint8_t> DrmHalTest::getKeyRequest(
+ const SessionId& sessionId,
+ const DrmHalVTSVendorModule_V1::ContentConfiguration& configuration,
+ const KeyType& type = KeyType::STREAMING) {
+ hidl_vec<uint8_t> keyRequest;
+ auto res = drmPlugin->getKeyRequest_1_2(
+ sessionId, configuration.initData, configuration.mimeType, type,
+ toHidlKeyedVector(configuration.optionalParameters),
+ [&](Status status, const hidl_vec<uint8_t>& request,
+ KeyRequestType requestType, const hidl_string&) {
+ EXPECT_EQ(Status::OK, status) << "Failed to get "
+ "key request for configuration "
+ << configuration.name;
+ if (type == KeyType::RELEASE) {
+ EXPECT_EQ(KeyRequestType::RELEASE, requestType);
+ } else {
+ EXPECT_EQ(KeyRequestType::INITIAL, requestType);
+ }
+ EXPECT_NE(request.size(), 0u) << "Expected key request size"
+ " to have length > 0 bytes";
+ keyRequest = request;
+ });
+ EXPECT_OK(res);
+ return keyRequest;
+}
+
+DrmHalVTSVendorModule_V1::ContentConfiguration DrmHalTest::getContent(const KeyType& type) const {
+ for (const auto& config : contentConfigurations) {
+ if (type != KeyType::OFFLINE || config.policy.allowOffline) {
+ return config;
+ }
+ }
+ EXPECT_TRUE(false) << "no content configurations found";
+ return {};
+}
+
+hidl_vec<uint8_t> DrmHalTest::provideKeyResponse(
+ const SessionId& sessionId,
+ const hidl_vec<uint8_t>& keyResponse) {
+ hidl_vec<uint8_t> keySetId;
+ auto res = drmPlugin->provideKeyResponse(
+ sessionId, keyResponse,
+ [&](StatusV1_0 status, const hidl_vec<uint8_t>& myKeySetId) {
+ EXPECT_EQ(StatusV1_0::OK, status) << "Failure providing "
+ "key response for configuration ";
+ keySetId = myKeySetId;
+ });
+ EXPECT_OK(res);
+ return keySetId;
+}
+
+/**
+ * Helper method to load keys for subsequent decrypt tests.
+ * These tests use predetermined key request/response to
+ * avoid requiring a round trip to a license server.
+ */
+hidl_vec<uint8_t> DrmHalTest::loadKeys(
+ const SessionId& sessionId,
+ const DrmHalVTSVendorModule_V1::ContentConfiguration& configuration,
+ const KeyType& type) {
+ hidl_vec<uint8_t> keyRequest = getKeyRequest(sessionId, configuration, type);
+
+ /**
+ * Get key response from vendor module
+ */
+ hidl_vec<uint8_t> keyResponse =
+ vendorModule->handleKeyRequest(keyRequest, configuration.serverUrl);
+ EXPECT_NE(keyResponse.size(), 0u) << "Expected key response size "
+ "to have length > 0 bytes";
+
+ return provideKeyResponse(sessionId, keyResponse);
+}
+
+hidl_vec<uint8_t> DrmHalTest::loadKeys(
+ const SessionId& sessionId,
+ const KeyType& type) {
+ return loadKeys(sessionId, getContent(type), type);
+}
+
+KeyedVector DrmHalTest::toHidlKeyedVector(
+ const map<string, string>& params) {
+ std::vector<KeyValue> stdKeyedVector;
+ for (auto it = params.begin(); it != params.end(); ++it) {
+ KeyValue keyValue;
+ keyValue.key = it->first;
+ keyValue.value = it->second;
+ stdKeyedVector.push_back(keyValue);
+ }
+ return KeyedVector(stdKeyedVector);
+}
+
+hidl_array<uint8_t, 16> DrmHalTest::toHidlArray(const vector<uint8_t>& vec) {
+ EXPECT_EQ(16u, vec.size());
+ return hidl_array<uint8_t, 16>(&vec[0]);
+}
+
+/**
+ * getDecryptMemory allocates memory for decryption, then sets it
+ * as a shared buffer base in the crypto hal. The allocated and
+ * mapped IMemory is returned.
+ *
+ * @param size the size of the memory segment to allocate
+ * @param the index of the memory segment which will be used
+ * to refer to it for decryption.
+ */
+sp<IMemory> DrmHalTest::getDecryptMemory(size_t size, size_t index) {
+ sp<IAllocator> ashmemAllocator = IAllocator::getService("ashmem");
+ EXPECT_NE(nullptr, ashmemAllocator.get());
+
+ hidl_memory hidlMemory;
+ auto res = ashmemAllocator->allocate(
+ size, [&](bool success, const hidl_memory& memory) {
+ EXPECT_EQ(success, true);
+ EXPECT_EQ(memory.size(), size);
+ hidlMemory = memory;
+ });
+
+ EXPECT_OK(res);
+
+ sp<IMemory> mappedMemory = mapMemory(hidlMemory);
+ EXPECT_NE(nullptr, mappedMemory.get());
+ res = cryptoPlugin->setSharedBufferBase(hidlMemory, index);
+ EXPECT_OK(res);
+ return mappedMemory;
+}
+
+void DrmHalTest::fillRandom(const sp<IMemory>& memory) {
+ random_device rd;
+ mt19937 rand(rd());
+ for (size_t i = 0; i < memory->getSize() / sizeof(uint32_t); i++) {
+ auto p = static_cast<uint32_t*>(
+ static_cast<void*>(memory->getPointer()));
+ p[i] = rand();
+ }
+}
+
+uint32_t DrmHalTest::decrypt(Mode mode, bool isSecure,
+ const hidl_array<uint8_t, 16>& keyId, uint8_t* iv,
+ const hidl_vec<SubSample>& subSamples, const Pattern& pattern,
+ const vector<uint8_t>& key, StatusV1_2 expectedStatus) {
+ const size_t kSegmentIndex = 0;
+
+ uint8_t localIv[AES_BLOCK_SIZE];
+ memcpy(localIv, iv, AES_BLOCK_SIZE);
+
+ size_t totalSize = 0;
+ for (size_t i = 0; i < subSamples.size(); i++) {
+ totalSize += subSamples[i].numBytesOfClearData;
+ totalSize += subSamples[i].numBytesOfEncryptedData;
+ }
+
+ // The first totalSize bytes of shared memory is the encrypted
+ // input, the second totalSize bytes (if exists) is the decrypted output.
+ size_t factor = expectedStatus == StatusV1_2::ERROR_DRM_FRAME_TOO_LARGE ? 1 : 2;
+ sp<IMemory> sharedMemory =
+ getDecryptMemory(totalSize * factor, kSegmentIndex);
+
+ const SharedBuffer sourceBuffer = {
+ .bufferId = kSegmentIndex, .offset = 0, .size = totalSize};
+ fillRandom(sharedMemory);
+
+ const DestinationBuffer destBuffer = {.type = BufferType::SHARED_MEMORY,
+ {.bufferId = kSegmentIndex,
+ .offset = totalSize,
+ .size = totalSize},
+ .secureMemory = nullptr};
+ const uint64_t offset = 0;
+ uint32_t bytesWritten = 0;
+ auto res = cryptoPlugin->decrypt_1_2(isSecure, keyId, localIv, mode, pattern,
+ subSamples, sourceBuffer, offset, destBuffer,
+ [&](StatusV1_2 status, uint32_t count, string detailedError) {
+ EXPECT_EQ(expectedStatus, status) << "Unexpected decrypt status " <<
+ detailedError;
+ bytesWritten = count;
+ });
+ EXPECT_OK(res);
+
+ if (bytesWritten != totalSize) {
+ return bytesWritten;
+ }
+ uint8_t* base = static_cast<uint8_t*>(
+ static_cast<void*>(sharedMemory->getPointer()));
+
+ // generate reference vector
+ vector<uint8_t> reference(totalSize);
+
+ memcpy(localIv, iv, AES_BLOCK_SIZE);
+ switch (mode) {
+ case Mode::UNENCRYPTED:
+ memcpy(&reference[0], base, totalSize);
+ break;
+ case Mode::AES_CTR:
+ aes_ctr_decrypt(&reference[0], base, localIv, subSamples, key);
+ break;
+ case Mode::AES_CBC:
+ aes_cbc_decrypt(&reference[0], base, localIv, subSamples, key);
+ break;
+ case Mode::AES_CBC_CTS:
+ EXPECT_TRUE(false) << "AES_CBC_CTS mode not supported";
+ break;
+ }
+
+ // compare reference to decrypted data which is at base + total size
+ EXPECT_EQ(0, memcmp(static_cast<void *>(&reference[0]),
+ static_cast<void*>(base + totalSize), totalSize))
+ << "decrypt data mismatch";
+ return totalSize;
+}
+
+/**
+ * Decrypt a list of clear+encrypted subsamples using the specified key
+ * in AES-CTR mode
+ */
+void DrmHalTest::aes_ctr_decrypt(uint8_t* dest, uint8_t* src,
+ uint8_t* iv, const hidl_vec<SubSample>& subSamples,
+ const vector<uint8_t>& key) {
+ AES_KEY decryptionKey;
+ AES_set_encrypt_key(&key[0], 128, &decryptionKey);
+
+ size_t offset = 0;
+ unsigned int blockOffset = 0;
+ uint8_t previousEncryptedCounter[AES_BLOCK_SIZE];
+ memset(previousEncryptedCounter, 0, AES_BLOCK_SIZE);
+
+ for (size_t i = 0; i < subSamples.size(); i++) {
+ const SubSample& subSample = subSamples[i];
+
+ if (subSample.numBytesOfClearData > 0) {
+ memcpy(dest + offset, src + offset, subSample.numBytesOfClearData);
+ offset += subSample.numBytesOfClearData;
+ }
+
+ if (subSample.numBytesOfEncryptedData > 0) {
+ AES_ctr128_encrypt(src + offset, dest + offset,
+ subSample.numBytesOfEncryptedData, &decryptionKey,
+ iv, previousEncryptedCounter, &blockOffset);
+ offset += subSample.numBytesOfEncryptedData;
+ }
+ }
+}
+
+/**
+ * Decrypt a list of clear+encrypted subsamples using the specified key
+ * in AES-CBC mode
+ */
+void DrmHalTest::aes_cbc_decrypt(uint8_t* dest, uint8_t* src,
+ uint8_t* iv, const hidl_vec<SubSample>& subSamples,
+ const vector<uint8_t>& key) {
+ AES_KEY decryptionKey;
+ AES_set_encrypt_key(&key[0], 128, &decryptionKey);
+
+ size_t offset = 0;
+ for (size_t i = 0; i < subSamples.size(); i++) {
+ memcpy(dest + offset, src + offset, subSamples[i].numBytesOfClearData);
+ offset += subSamples[i].numBytesOfClearData;
+
+ AES_cbc_encrypt(src + offset, dest + offset, subSamples[i].numBytesOfEncryptedData,
+ &decryptionKey, iv, 0 /* decrypt */);
+ offset += subSamples[i].numBytesOfEncryptedData;
+ }
+}
+
+/**
+ * Helper method to test decryption with invalid keys is returned
+ */
+void DrmHalClearkeyTest::decryptWithInvalidKeys(
+ hidl_vec<uint8_t>& invalidResponse,
+ vector<uint8_t>& iv,
+ const Pattern& noPattern,
+ const vector<SubSample>& subSamples) {
+ DrmHalVTSVendorModule_V1::ContentConfiguration content = getContent();
+ if (content.keys.empty()) {
+ FAIL() << "no keys";
+ }
+
+ const auto& key = content.keys[0];
+ auto sessionId = openSession();
+ auto res = drmPlugin->provideKeyResponse(
+ sessionId, invalidResponse,
+ [&](StatusV1_0 status, const hidl_vec<uint8_t>& myKeySetId) {
+ EXPECT_EQ(StatusV1_0::OK, status);
+ EXPECT_EQ(0u, myKeySetId.size());
+ });
+ EXPECT_OK(res);
+
+ EXPECT_TRUE(cryptoPlugin->setMediaDrmSession(sessionId).isOk());
+
+ uint32_t byteCount = decrypt(Mode::AES_CTR, key.isSecure,
+ toHidlArray(key.keyId), &iv[0], subSamples, noPattern,
+ key.clearContentKey, Status::ERROR_DRM_NO_LICENSE);
+ EXPECT_EQ(0u, byteCount);
+
+ closeSession(sessionId);
+}
+
+} // namespace vts
+} // namespace V1_2
+} // namespace drm
+} // namespace hardware
+} // namespace android
diff --git a/drm/1.2/vts/functional/drm_hal_common.h b/drm/1.2/vts/functional/drm_hal_common.h
new file mode 100644
index 000000000..e34866474
--- /dev/null
+++ b/drm/1.2/vts/functional/drm_hal_common.h
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DRM_HAL_COMMON_H
+#define DRM_HAL_COMMON_H
+
+#include <android/hardware/drm/1.2/ICryptoFactory.h>
+#include <android/hardware/drm/1.2/ICryptoPlugin.h>
+#include <android/hardware/drm/1.2/IDrmFactory.h>
+#include <android/hardware/drm/1.2/IDrmPlugin.h>
+#include <android/hardware/drm/1.2/IDrmPluginListener.h>
+#include <android/hardware/drm/1.2/types.h>
+
+#include <chrono>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "drm_hal_vendor_module_api.h"
+#include "vendor_modules.h"
+#include "VtsHalHidlTargetCallbackBase.h"
+#include "VtsHalHidlTargetTestBase.h"
+
+using ::android::hardware::drm::V1_0::EventType;
+using ::android::hardware::drm::V1_0::KeyedVector;
+using KeyStatusV1_0 = ::android::hardware::drm::V1_0::KeyStatus;
+using ::android::hardware::drm::V1_0::KeyType;
+using ::android::hardware::drm::V1_0::Mode;
+using ::android::hardware::drm::V1_0::Pattern;
+using ::android::hardware::drm::V1_0::SessionId;
+using ::android::hardware::drm::V1_0::SubSample;
+
+using ::android::hardware::drm::V1_1::ICryptoFactory;
+
+using StatusV1_2 = ::android::hardware::drm::V1_2::Status;
+
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+
+using ::android::hidl::memory::V1_0::IMemory;
+using ::android::sp;
+
+using ::testing::VtsHalHidlTargetTestBase;
+
+using std::map;
+using std::string;
+using std::unique_ptr;
+using std::vector;
+
+#define EXPECT_OK(ret) EXPECT_TRUE(ret.isOk())
+
+#define RETURN_IF_SKIPPED \
+ if (!vendorModule->isInstalled()) { \
+ std::cout << "[ SKIPPED ] This drm scheme not supported." << \
+ " library:" << GetParam() << " service-name:" << \
+ vendorModule->getServiceName() << std::endl; \
+ return; \
+ }
+
+namespace android {
+namespace hardware {
+namespace drm {
+namespace V1_2 {
+namespace vts {
+
+class DrmHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
+ public:
+ // get the test environment singleton
+ static DrmHidlEnvironment* Instance() {
+ static DrmHidlEnvironment* instance = new DrmHidlEnvironment;
+ return instance;
+ }
+
+ void registerTestServices() override {
+ registerTestService<ICryptoFactory>();
+ registerTestService<IDrmFactory>();
+ setServiceCombMode(::testing::HalServiceCombMode::NO_COMBINATION);
+ }
+
+ private:
+ DrmHidlEnvironment() {}
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(DrmHidlEnvironment);
+};
+
+class DrmHalTest : public ::testing::TestWithParam<std::string> {
+ public:
+ static drm_vts::VendorModules* gVendorModules;
+ DrmHalTest();
+ virtual void SetUp() override;
+ virtual void TearDown() override {}
+
+ protected:
+ hidl_array<uint8_t, 16> getVendorUUID();
+ SessionId openSession();
+ void closeSession(const SessionId& sessionId);
+ hidl_vec<uint8_t> loadKeys(const SessionId& sessionId,
+ const KeyType& type = KeyType::STREAMING);
+ hidl_vec<uint8_t> loadKeys(const SessionId& sessionId,
+ const DrmHalVTSVendorModule_V1::ContentConfiguration&,
+ const KeyType& type = KeyType::STREAMING);
+ hidl_vec<uint8_t> getKeyRequest(const SessionId& sessionId,
+ const DrmHalVTSVendorModule_V1::ContentConfiguration&,
+ const KeyType& type);
+ hidl_vec<uint8_t> provideKeyResponse(const SessionId& sessionId,
+ const hidl_vec<uint8_t>& keyResponse);
+ DrmHalVTSVendorModule_V1::ContentConfiguration getContent(
+ const KeyType& type = KeyType::STREAMING) const;
+
+ KeyedVector toHidlKeyedVector(const map<string, string>& params);
+ hidl_array<uint8_t, 16> toHidlArray(const vector<uint8_t>& vec);
+
+ void fillRandom(const sp<IMemory>& memory);
+ sp<IMemory> getDecryptMemory(size_t size, size_t index);
+ uint32_t decrypt(Mode mode, bool isSecure,
+ const hidl_array<uint8_t, 16>& keyId, uint8_t* iv,
+ const hidl_vec<SubSample>& subSamples, const Pattern& pattern,
+ const vector<uint8_t>& key, StatusV1_2 expectedStatus);
+ void aes_ctr_decrypt(uint8_t* dest, uint8_t* src, uint8_t* iv,
+ const hidl_vec<SubSample>& subSamples, const vector<uint8_t>& key);
+ void aes_cbc_decrypt(uint8_t* dest, uint8_t* src, uint8_t* iv,
+ const hidl_vec<SubSample>& subSamples, const vector<uint8_t>& key);
+
+ sp<IDrmFactory> drmFactory;
+ sp<ICryptoFactory> cryptoFactory;
+ sp<IDrmPlugin> drmPlugin;
+ sp<ICryptoPlugin> cryptoPlugin;
+ unique_ptr<DrmHalVTSVendorModule_V1> vendorModule;
+ const vector<DrmHalVTSVendorModule_V1::ContentConfiguration> contentConfigurations;
+
+ private:
+ sp<IDrmPlugin> createDrmPlugin();
+ sp<ICryptoPlugin> createCryptoPlugin();
+
+};
+
+class DrmHalClearkeyTest : public DrmHalTest {
+ public:
+ virtual void SetUp() override { DrmHalTest::SetUp(); }
+ virtual void TearDown() override {}
+ void decryptWithInvalidKeys(hidl_vec<uint8_t>& invalidResponse,
+ vector<uint8_t>& iv, const Pattern& noPattern, const vector<SubSample>& subSamples);
+};
+
+/**
+ * Event Handling tests
+ */
+extern const char *kCallbackLostState;
+extern const char *kCallbackKeysChange;
+
+struct ListenerEventArgs {
+ SessionId sessionId;
+ hidl_vec<KeyStatus> keyStatusList;
+ bool hasNewUsableKey;
+};
+
+class DrmHalPluginListener
+ : public ::testing::VtsHalHidlTargetCallbackBase<ListenerEventArgs>,
+ public IDrmPluginListener {
+public:
+ DrmHalPluginListener() {
+ SetWaitTimeoutDefault(std::chrono::milliseconds(500));
+ }
+ virtual ~DrmHalPluginListener() {}
+
+ virtual Return<void> sendEvent(EventType, const hidl_vec<uint8_t>&,
+ const hidl_vec<uint8_t>& ) override { return Void(); }
+
+ virtual Return<void> sendExpirationUpdate(const hidl_vec<uint8_t>&,
+ int64_t) override { return Void(); }
+
+ virtual Return<void> sendKeysChange(const hidl_vec<uint8_t>&,
+ const hidl_vec<KeyStatusV1_0>&, bool) override { return Void(); }
+
+ virtual Return<void> sendSessionLostState(const hidl_vec<uint8_t>& sessionId) override;
+
+ virtual Return<void> sendKeysChange_1_2(const hidl_vec<uint8_t>&,
+ const hidl_vec<KeyStatus>&, bool) override;
+
+};
+
+} // namespace vts
+} // namespace V1_2
+} // namespace drm
+} // namespace hardware
+} // namespace android
+
+#endif // DRM_HAL_COMMON_H
diff --git a/drm/1.2/vts/functional/drm_hal_test.cpp b/drm/1.2/vts/functional/drm_hal_test.cpp
new file mode 100644
index 000000000..37ecc2543
--- /dev/null
+++ b/drm/1.2/vts/functional/drm_hal_test.cpp
@@ -0,0 +1,591 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "drm_hal_test@1.2"
+
+#include <gtest/gtest.h>
+#include <hidl/HidlSupport.h>
+#include <log/log.h>
+#include <openssl/aes.h>
+
+#include "drm_hal_common.h"
+
+using ::android::hardware::drm::V1_0::Status;
+using ::android::hardware::drm::V1_1::KeyRequestType;
+using ::android::hardware::drm::V1_1::SecurityLevel;
+using ::android::hardware::drm::V1_2::HdcpLevel;
+using ::android::hardware::drm::V1_2::KeySetId;
+using ::android::hardware::drm::V1_2::KeyStatus;
+using ::android::hardware::drm::V1_2::KeyStatusType;
+using ::android::hardware::drm::V1_2::OfflineLicenseState;
+
+using ::android::hardware::drm::V1_2::vts::DrmHalClearkeyTest;
+using ::android::hardware::drm::V1_2::vts::DrmHalPluginListener;
+using ::android::hardware::drm::V1_2::vts::DrmHalTest;
+using ::android::hardware::drm::V1_2::vts::DrmHidlEnvironment;
+using ::android::hardware::drm::V1_2::vts::kCallbackLostState;
+using ::android::hardware::drm::V1_2::vts::kCallbackKeysChange;
+
+using ::android::hardware::hidl_string;
+
+static const char* const kVideoMp4 = "video/mp4";
+static const char* const kBadMime = "video/unknown";
+static const char* const kDrmErrorTestKey = "drmErrorTest";
+static const char* const kDrmErrorInvalidState = "invalidState";
+static const char* const kDrmErrorResourceContention = "resourceContention";
+static const SecurityLevel kSwSecureCrypto = SecurityLevel::SW_SECURE_CRYPTO;
+
+/**
+ * Ensure drm factory supports module UUID Scheme
+ */
+TEST_P(DrmHalTest, VendorUuidSupported) {
+ auto res = drmFactory->isCryptoSchemeSupported_1_2(getVendorUUID(), kVideoMp4, kSwSecureCrypto);
+ ALOGI("kVideoMp4 = %s res %d", kVideoMp4, (bool)res);
+ EXPECT_TRUE(res);
+}
+
+/**
+ * Ensure drm factory doesn't support an invalid scheme UUID
+ */
+TEST_P(DrmHalTest, InvalidPluginNotSupported) {
+ const uint8_t kInvalidUUID[16] = {
+ 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
+ 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80};
+ EXPECT_FALSE(drmFactory->isCryptoSchemeSupported_1_2(kInvalidUUID, kVideoMp4, kSwSecureCrypto));
+}
+
+/**
+ * Ensure drm factory doesn't support an empty UUID
+ */
+TEST_P(DrmHalTest, EmptyPluginUUIDNotSupported) {
+ hidl_array<uint8_t, 16> emptyUUID;
+ memset(emptyUUID.data(), 0, 16);
+ EXPECT_FALSE(drmFactory->isCryptoSchemeSupported_1_2(emptyUUID, kVideoMp4, kSwSecureCrypto));
+}
+
+/**
+ * Ensure drm factory doesn't support an invalid mime type
+ */
+TEST_P(DrmHalTest, BadMimeNotSupported) {
+ EXPECT_FALSE(drmFactory->isCryptoSchemeSupported_1_2(getVendorUUID(), kBadMime, kSwSecureCrypto));
+}
+
+/**
+ * DrmPlugin tests
+ */
+
+/**
+ * Test that a DRM plugin can handle provisioning. While
+ * it is not required that a DRM scheme require provisioning,
+ * it should at least return appropriate status values. If
+ * a provisioning request is returned, it is passed to the
+ * vendor module which should provide a provisioning response
+ * that is delivered back to the HAL.
+ */
+TEST_P(DrmHalTest, DoProvisioning) {
+ RETURN_IF_SKIPPED;
+ hidl_string certificateType;
+ hidl_string certificateAuthority;
+ hidl_vec<uint8_t> provisionRequest;
+ hidl_string defaultUrl;
+ auto res = drmPlugin->getProvisionRequest_1_2(
+ certificateType, certificateAuthority,
+ [&](StatusV1_2 status, const hidl_vec<uint8_t>& request,
+ const hidl_string& url) {
+ if (status == StatusV1_2::OK) {
+ EXPECT_NE(request.size(), 0u);
+ provisionRequest = request;
+ defaultUrl = url;
+ } else if (status == StatusV1_2::ERROR_DRM_CANNOT_HANDLE) {
+ EXPECT_EQ(0u, request.size());
+ }
+ });
+ EXPECT_OK(res);
+
+ if (provisionRequest.size() > 0) {
+ vector<uint8_t> response = vendorModule->handleProvisioningRequest(
+ provisionRequest, defaultUrl);
+ ASSERT_NE(0u, response.size());
+
+ auto res = drmPlugin->provideProvisionResponse(
+ response, [&](Status status, const hidl_vec<uint8_t>&,
+ const hidl_vec<uint8_t>&) {
+ EXPECT_EQ(Status::OK, status);
+ });
+ EXPECT_OK(res);
+ }
+}
+
+/**
+ * A get key request should fail if no sessionId is provided
+ */
+TEST_P(DrmHalTest, GetKeyRequestNoSession) {
+ SessionId invalidSessionId;
+ hidl_vec<uint8_t> initData;
+ KeyedVector optionalParameters;
+ auto res = drmPlugin->getKeyRequest_1_2(
+ invalidSessionId, initData, kVideoMp4, KeyType::STREAMING,
+ optionalParameters,
+ [&](StatusV1_2 status, const hidl_vec<uint8_t>&, KeyRequestType,
+ const hidl_string&) { EXPECT_EQ(StatusV1_2::BAD_VALUE, status); });
+ EXPECT_OK(res);
+}
+
+/**
+ * Test that the plugin returns the documented error for the
+ * case of attempting to generate a key request using an
+ * invalid mime type
+ */
+TEST_P(DrmHalTest, GetKeyRequestBadMime) {
+ auto sessionId = openSession();
+ hidl_vec<uint8_t> initData;
+ KeyedVector optionalParameters;
+ auto res = drmPlugin->getKeyRequest_1_2(
+ sessionId, initData, kBadMime, KeyType::STREAMING,
+ optionalParameters, [&](StatusV1_2 status, const hidl_vec<uint8_t>&,
+ KeyRequestType, const hidl_string&) {
+ EXPECT_NE(StatusV1_2::OK, status);
+ });
+ EXPECT_OK(res);
+ closeSession(sessionId);
+}
+
+template <Status S, size_t N>
+void checkKeySetIds(Status status, const hidl_vec<KeySetId>& keySetIds) {
+ EXPECT_EQ(S, status);
+ if (S == Status::OK) {
+ EXPECT_EQ(N, keySetIds.size());
+ }
+}
+
+template <Status S, OfflineLicenseState T>
+void checkKeySetIdState(Status status, OfflineLicenseState state) {
+ if (S == Status::OK) {
+ EXPECT_TRUE(T == OfflineLicenseState::USABLE || T == OfflineLicenseState::INACTIVE);
+ } else {
+ EXPECT_TRUE(T == OfflineLicenseState::UNKNOWN);
+ }
+ EXPECT_EQ(S, status);
+ EXPECT_EQ(T, state);
+}
+
+/**
+ * Test drm plugin offline key support
+ */
+TEST_P(DrmHalTest, OfflineLicenseTest) {
+ auto sessionId = openSession();
+ hidl_vec<uint8_t> keySetId = loadKeys(sessionId, KeyType::OFFLINE);
+
+ auto res = drmPlugin->getOfflineLicenseKeySetIds(
+ [&](Status status, const hidl_vec<KeySetId>& keySetIds) {
+ bool found = false;
+ EXPECT_EQ(Status::OK, status);
+ for (KeySetId keySetId2: keySetIds) {
+ if (keySetId == keySetId2) {
+ found = true;
+ break;
+ }
+ }
+ EXPECT_TRUE(found) << "keySetId not found";
+ });
+ EXPECT_OK(res);
+
+ Status err = drmPlugin->removeOfflineLicense(keySetId);
+ EXPECT_EQ(Status::OK, err);
+
+ res = drmPlugin->getOfflineLicenseKeySetIds(
+ [&](Status status, const hidl_vec<KeySetId>& keySetIds) {
+ EXPECT_EQ(Status::OK, status);
+ for (KeySetId keySetId2: keySetIds) {
+ EXPECT_NE(keySetId, keySetId2);
+ }
+ });
+ EXPECT_OK(res);
+
+ err = drmPlugin->removeOfflineLicense(keySetId);
+ EXPECT_EQ(Status::BAD_VALUE, err);
+
+ closeSession(sessionId);
+}
+
+/**
+ * Test drm plugin offline key state
+ */
+TEST_P(DrmHalTest, OfflineLicenseStateTest) {
+ auto sessionId = openSession();
+ DrmHalVTSVendorModule_V1::ContentConfiguration content = getContent(KeyType::OFFLINE);
+ hidl_vec<uint8_t> keySetId = loadKeys(sessionId, content, KeyType::OFFLINE);
+ drmPlugin->getOfflineLicenseState(keySetId, checkKeySetIdState<Status::OK, OfflineLicenseState::USABLE>);
+
+ hidl_vec<uint8_t> keyRequest = getKeyRequest(keySetId, content, KeyType::RELEASE);
+ drmPlugin->getOfflineLicenseState(keySetId, checkKeySetIdState<Status::OK, OfflineLicenseState::INACTIVE>);
+
+ /**
+ * Get key response from vendor module
+ */
+ hidl_vec<uint8_t> keyResponse =
+ vendorModule->handleKeyRequest(keyRequest, content.serverUrl);
+ EXPECT_GT(keyResponse.size(), 0u);
+
+ provideKeyResponse(keySetId, keyResponse);
+ drmPlugin->getOfflineLicenseState(keySetId, checkKeySetIdState<Status::BAD_VALUE, OfflineLicenseState::UNKNOWN>);
+ closeSession(sessionId);
+}
+
+/**
+ * Negative offline license test. Remove empty keySetId
+ */
+TEST_P(DrmHalTest, RemoveEmptyKeySetId) {
+ KeySetId emptyKeySetId;
+ Status err = drmPlugin->removeOfflineLicense(emptyKeySetId);
+ EXPECT_EQ(Status::BAD_VALUE, err);
+}
+
+/**
+ * Negative offline license test. Get empty keySetId state
+ */
+TEST_P(DrmHalTest, GetEmptyKeySetIdState) {
+ KeySetId emptyKeySetId;
+ auto res = drmPlugin->getOfflineLicenseState(emptyKeySetId, checkKeySetIdState<Status::BAD_VALUE, OfflineLicenseState::UNKNOWN>);
+ EXPECT_OK(res);
+}
+
+/**
+ * Test that the plugin returns valid connected and max HDCP levels
+ */
+TEST_P(DrmHalTest, GetHdcpLevels) {
+ auto res = drmPlugin->getHdcpLevels_1_2(
+ [&](StatusV1_2 status, const HdcpLevel &connectedLevel,
+ const HdcpLevel &maxLevel) {
+ EXPECT_EQ(StatusV1_2::OK, status);
+ EXPECT_GE(connectedLevel, HdcpLevel::HDCP_NONE);
+ EXPECT_LE(maxLevel, HdcpLevel::HDCP_V2_3);
+ });
+ EXPECT_OK(res);
+}
+
+/**
+ * Simulate the plugin sending keys change and make sure
+ * the listener gets them.
+ */
+TEST_P(DrmHalTest, ListenerKeysChange) {
+ RETURN_IF_SKIPPED;
+ sp<DrmHalPluginListener> listener = new DrmHalPluginListener();
+ auto res = drmPlugin->setListener(listener);
+ EXPECT_OK(res);
+
+ auto sessionId = openSession();
+ const hidl_vec<KeyStatus> keyStatusList = {
+ {{1}, KeyStatusType::USABLE},
+ {{2}, KeyStatusType::EXPIRED},
+ {{3}, KeyStatusType::OUTPUTNOTALLOWED},
+ {{4}, KeyStatusType::STATUSPENDING},
+ {{5}, KeyStatusType::INTERNALERROR},
+ {{6}, KeyStatusType::USABLEINFUTURE},
+ };
+
+ drmPlugin->sendKeysChange_1_2(sessionId, keyStatusList, true);
+ auto result = listener->WaitForCallback(kCallbackKeysChange);
+ EXPECT_TRUE(result.no_timeout);
+ EXPECT_TRUE(result.args);
+ EXPECT_EQ(sessionId, result.args->sessionId);
+ EXPECT_EQ(keyStatusList, result.args->keyStatusList);
+ closeSession(sessionId);
+}
+
+/**
+ * CryptoPlugin Decrypt tests
+ */
+
+/**
+ * Positive decrypt test. "Decrypt" a single clear segment
+ */
+TEST_P(DrmHalTest, ClearSegmentTest) {
+ RETURN_IF_SKIPPED;
+ for (const auto& config : contentConfigurations) {
+ for (const auto& key : config.keys) {
+ const size_t kSegmentSize = 1024;
+ vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
+ const Pattern noPattern = {0, 0};
+ const vector<SubSample> subSamples = {{.numBytesOfClearData = kSegmentSize,
+ .numBytesOfEncryptedData = 0}};
+ auto sessionId = openSession();
+ loadKeys(sessionId, config);
+
+ Status status = cryptoPlugin->setMediaDrmSession(sessionId);
+ EXPECT_EQ(Status::OK, status);
+
+ uint32_t byteCount = decrypt(Mode::UNENCRYPTED, key.isSecure, toHidlArray(key.keyId),
+ &iv[0], subSamples, noPattern, key.clearContentKey, StatusV1_2::OK);
+ EXPECT_EQ(kSegmentSize, byteCount);
+
+ closeSession(sessionId);
+ }
+ }
+}
+
+/**
+ * Positive decrypt test. Decrypt a single segment using aes_ctr.
+ * Verify data matches.
+ */
+TEST_P(DrmHalTest, EncryptedAesCtrSegmentTest) {
+ RETURN_IF_SKIPPED;
+ for (const auto& config : contentConfigurations) {
+ for (const auto& key : config.keys) {
+ const size_t kSegmentSize = 1024;
+ vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
+ const Pattern noPattern = {0, 0};
+ const vector<SubSample> subSamples = {{.numBytesOfClearData = kSegmentSize,
+ .numBytesOfEncryptedData = 0}};
+ auto sessionId = openSession();
+ loadKeys(sessionId, config);
+
+ Status status = cryptoPlugin->setMediaDrmSession(sessionId);
+ EXPECT_EQ(Status::OK, status);
+
+ uint32_t byteCount = decrypt(Mode::AES_CTR, key.isSecure, toHidlArray(key.keyId),
+ &iv[0], subSamples, noPattern, key.clearContentKey, StatusV1_2::OK);
+ EXPECT_EQ(kSegmentSize, byteCount);
+
+ closeSession(sessionId);
+ }
+ }
+}
+
+/**
+ * Negative decrypt test. Decrypted frame too large to fit in output buffer
+ */
+TEST_P(DrmHalTest, ErrorFrameTooLarge) {
+ RETURN_IF_SKIPPED;
+ for (const auto& config : contentConfigurations) {
+ for (const auto& key : config.keys) {
+ const size_t kSegmentSize = 1024;
+ vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
+ const Pattern noPattern = {0, 0};
+ const vector<SubSample> subSamples = {{.numBytesOfClearData = kSegmentSize,
+ .numBytesOfEncryptedData = 0}};
+ auto sessionId = openSession();
+ loadKeys(sessionId, config);
+
+ Status status = cryptoPlugin->setMediaDrmSession(sessionId);
+ EXPECT_EQ(Status::OK, status);
+
+ decrypt(Mode::UNENCRYPTED, key.isSecure, toHidlArray(key.keyId),
+ &iv[0], subSamples, noPattern, key.clearContentKey, StatusV1_2::ERROR_DRM_FRAME_TOO_LARGE);
+
+ closeSession(sessionId);
+ }
+ }
+}
+
+/**
+ * Negative decrypt test. Decrypt without loading keys.
+ */
+TEST_P(DrmHalTest, EncryptedAesCtrSegmentTestNoKeys) {
+ RETURN_IF_SKIPPED;
+ for (const auto& config : contentConfigurations) {
+ for (const auto& key : config.keys) {
+ vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
+ const Pattern noPattern = {0, 0};
+ const vector<SubSample> subSamples = {{.numBytesOfClearData = 256,
+ .numBytesOfEncryptedData = 256}};
+ auto sessionId = openSession();
+
+ Status status = cryptoPlugin->setMediaDrmSession(sessionId);
+ EXPECT_EQ(Status::OK, status);
+
+ uint32_t byteCount = decrypt(Mode::AES_CTR, key.isSecure,
+ toHidlArray(key.keyId), &iv[0], subSamples, noPattern,
+ key.clearContentKey, StatusV1_2::ERROR_DRM_NO_LICENSE);
+ EXPECT_EQ(0u, byteCount);
+
+ closeSession(sessionId);
+ }
+ }
+}
+
+/**
+ * Ensure clearkey drm factory doesn't support security level higher than supported
+ */
+TEST_P(DrmHalClearkeyTest, BadLevelNotSupported) {
+ const SecurityLevel kHwSecureAll = SecurityLevel::HW_SECURE_ALL;
+ EXPECT_FALSE(drmFactory->isCryptoSchemeSupported_1_2(getVendorUUID(), kVideoMp4, kHwSecureAll));
+}
+
+/**
+ * Test resource contention during attempt to generate key request
+ */
+TEST_P(DrmHalClearkeyTest, GetKeyRequestResourceContention) {
+ Status status = drmPlugin->setPropertyString(kDrmErrorTestKey, kDrmErrorResourceContention);
+ EXPECT_EQ(Status::OK, status);
+ auto sessionId = openSession();
+ hidl_vec<uint8_t> initData;
+ KeyedVector optionalParameters;
+ auto res = drmPlugin->getKeyRequest_1_2(
+ sessionId, initData, kVideoMp4, KeyType::STREAMING,
+ optionalParameters, [&](StatusV1_2 status, const hidl_vec<uint8_t>&,
+ KeyRequestType, const hidl_string&) {
+ EXPECT_EQ(StatusV1_2::ERROR_DRM_RESOURCE_CONTENTION, status);
+ });
+ EXPECT_OK(res);
+
+ status = drmPlugin->closeSession(sessionId);
+ EXPECT_NE(Status::OK, status);
+}
+
+/**
+ * Test clearkey plugin offline key with mock error
+ */
+TEST_P(DrmHalClearkeyTest, OfflineLicenseInvalidState) {
+ auto sessionId = openSession();
+ hidl_vec<uint8_t> keySetId = loadKeys(sessionId, KeyType::OFFLINE);
+ Status status = drmPlugin->setPropertyString(kDrmErrorTestKey, kDrmErrorInvalidState);
+ EXPECT_EQ(Status::OK, status);
+
+ // everything should start failing
+ const Status kInvalidState = Status::ERROR_DRM_INVALID_STATE;
+ const OfflineLicenseState kUnknownState = OfflineLicenseState::UNKNOWN;
+ auto res = drmPlugin->getOfflineLicenseKeySetIds(checkKeySetIds<kInvalidState, 0u>);
+ EXPECT_OK(res);
+ res = drmPlugin->getOfflineLicenseState(keySetId, checkKeySetIdState<kInvalidState, kUnknownState>);
+ EXPECT_OK(res);
+ Status err = drmPlugin->removeOfflineLicense(keySetId);
+ EXPECT_EQ(kInvalidState, err);
+ closeSession(sessionId);
+}
+
+/**
+ * Test SessionLostState is triggered on error
+ */
+TEST_P(DrmHalClearkeyTest, SessionLostState) {
+ sp<DrmHalPluginListener> listener = new DrmHalPluginListener();
+ auto res = drmPlugin->setListener(listener);
+ EXPECT_OK(res);
+
+ Status status = drmPlugin->setPropertyString(kDrmErrorTestKey, kDrmErrorInvalidState);
+ EXPECT_EQ(Status::OK, status);
+
+ auto sessionId = openSession();
+ drmPlugin->closeSession(sessionId);
+
+ auto result = listener->WaitForCallback(kCallbackLostState);
+ EXPECT_TRUE(result.no_timeout);
+ EXPECT_TRUE(result.args);
+ EXPECT_EQ(sessionId, result.args->sessionId);
+}
+
+/**
+ * Negative decrypt test. Decrypt with invalid key.
+ */
+TEST_P(DrmHalClearkeyTest, DecryptWithEmptyKey) {
+ vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
+ const Pattern noPattern = {0, 0};
+ const uint32_t kClearBytes = 512;
+ const uint32_t kEncryptedBytes = 512;
+ const vector<SubSample> subSamples = {
+ {.numBytesOfClearData = kClearBytes,
+ .numBytesOfEncryptedData = kEncryptedBytes}};
+
+ // base 64 encoded JSON response string, must not contain padding character '='
+ const hidl_string emptyKeyResponse =
+ "{\"keys\":[" \
+ "{" \
+ "\"kty\":\"oct\"" \
+ "\"alg\":\"A128KW2\"" \
+ "\"k\":\"SGVsbG8gRnJpZW5kIQ\"" \
+ "\"kid\":\"Y2xlYXJrZXlrZXlpZDAyAy\"" \
+ "}" \
+ "{" \
+ "\"kty\":\"oct\"," \
+ "\"alg\":\"A128KW2\"" \
+ "\"kid\":\"Y2xlYXJrZXlrZXlpZDAzAy\"," \
+ // empty key follows
+ "\"k\":\"R\"" \
+ "}]" \
+ "}";
+ const size_t kEmptyKeyResponseSize = emptyKeyResponse.size();
+
+ hidl_vec<uint8_t> invalidResponse;
+ invalidResponse.resize(kEmptyKeyResponseSize);
+ memcpy(invalidResponse.data(), emptyKeyResponse.c_str(), kEmptyKeyResponseSize);
+ decryptWithInvalidKeys(invalidResponse, iv, noPattern, subSamples);
+}
+
+/**
+ * Negative decrypt test. Decrypt with a key exceeds AES_BLOCK_SIZE.
+ */
+TEST_P(DrmHalClearkeyTest, DecryptWithKeyTooLong) {
+ vector<uint8_t> iv(AES_BLOCK_SIZE, 0);
+ const Pattern noPattern = {0, 0};
+ const uint32_t kClearBytes = 512;
+ const uint32_t kEncryptedBytes = 512;
+ const vector<SubSample> subSamples = {
+ {.numBytesOfClearData = kClearBytes,
+ .numBytesOfEncryptedData = kEncryptedBytes}};
+
+ // base 64 encoded JSON response string, must not contain padding character '='
+ const hidl_string keyTooLongResponse =
+ "{\"keys\":[" \
+ "{" \
+ "\"kty\":\"oct\"," \
+ "\"alg\":\"A128KW2\"" \
+ "\"kid\":\"Y2xlYXJrZXlrZXlpZDAzAy\"," \
+ // key too long
+ "\"k\":\"V2lubmllIHRoZSBwb29oIVdpbm5pZSB0aGUgcG9vaCE=\"" \
+ "}]" \
+ "}";
+ const size_t kKeyTooLongResponseSize = keyTooLongResponse.size();
+
+ hidl_vec<uint8_t> invalidResponse;
+ invalidResponse.resize(kKeyTooLongResponseSize);
+ memcpy(invalidResponse.data(), keyTooLongResponse.c_str(), kKeyTooLongResponseSize);
+ decryptWithInvalidKeys(invalidResponse, iv, noPattern, subSamples);
+}
+
+/**
+ * Instantiate the set of test cases for each vendor module
+ */
+
+INSTANTIATE_TEST_CASE_P(
+ DrmHalTestClearkey, DrmHalTest,
+ testing::Values("clearkey"));
+
+INSTANTIATE_TEST_CASE_P(
+ DrmHalTestClearkeyExtended, DrmHalClearkeyTest,
+ testing::Values("clearkey"));
+
+INSTANTIATE_TEST_CASE_P(
+ DrmHalTestVendor, DrmHalTest,
+ testing::ValuesIn(DrmHalTest::gVendorModules->getPathList()));
+
+int main(int argc, char** argv) {
+#if defined(__LP64__)
+ const char* kModulePath = "/data/local/tmp/64/lib";
+#else
+ const char* kModulePath = "/data/local/tmp/32/lib";
+#endif
+ DrmHalTest::gVendorModules = new drm_vts::VendorModules(kModulePath);
+ if (DrmHalTest::gVendorModules->getPathList().size() == 0) {
+ std::cerr << "WARNING: No vendor modules found in " << kModulePath <<
+ ", all vendor tests will be skipped" << std::endl;
+ }
+ ::testing::AddGlobalTestEnvironment(DrmHidlEnvironment::Instance());
+ ::testing::InitGoogleTest(&argc, argv);
+ DrmHidlEnvironment::Instance()->init(&argc, argv);
+ int status = RUN_ALL_TESTS();
+ ALOGI("Test result = %d", status);
+ return status;
+}
diff --git a/drm/1.2/vts/functional/vendor_modules.cpp b/drm/1.2/vts/functional/vendor_modules.cpp
new file mode 100644
index 000000000..efcb90a91
--- /dev/null
+++ b/drm/1.2/vts/functional/vendor_modules.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "drm-vts-vendor-modules"
+
+#include <dirent.h>
+#include <dlfcn.h>
+#include <log/log.h>
+#include <memory>
+#include <utils/String8.h>
+#include <SharedLibrary.h>
+
+#include "vendor_modules.h"
+
+using std::string;
+using std::vector;
+using std::unique_ptr;
+using ::android::String8;
+using ::android::hardware::drm::V1_0::helper::SharedLibrary;
+
+namespace drm_vts {
+void VendorModules::scanModules(const std::string &directory) {
+ DIR* dir = opendir(directory.c_str());
+ if (dir == NULL) {
+ ALOGE("Unable to open drm VTS vendor directory %s", directory.c_str());
+ } else {
+ struct dirent* entry;
+ while ((entry = readdir(dir))) {
+ ALOGD("checking file %s", entry->d_name);
+ string fullpath = directory + "/" + entry->d_name;
+ if (endsWith(fullpath, ".so")) {
+ mPathList.push_back(fullpath);
+ }
+ }
+ closedir(dir);
+ }
+}
+
+DrmHalVTSVendorModule* VendorModules::getModule(const string& path) {
+ if (mOpenLibraries.find(path) == mOpenLibraries.end()) {
+ auto library = std::make_unique<SharedLibrary>(String8(path.c_str()));
+ if (!library) {
+ ALOGE("failed to map shared library %s", path.c_str());
+ return NULL;
+ }
+ mOpenLibraries[path] = std::move(library);
+ }
+ const unique_ptr<SharedLibrary>& library = mOpenLibraries[path];
+ void* symbol = library->lookup("vendorModuleFactory");
+ if (symbol == NULL) {
+ ALOGE("getVendorModule failed to lookup 'vendorModuleFactory' in %s: "
+ "%s", path.c_str(), library->lastError());
+ return NULL;
+ }
+ typedef DrmHalVTSVendorModule* (*ModuleFactory)();
+ ModuleFactory moduleFactory = reinterpret_cast<ModuleFactory>(symbol);
+ return (*moduleFactory)();
+}
+};
diff --git a/drm/1.2/vts/functional/vendor_modules.h b/drm/1.2/vts/functional/vendor_modules.h
new file mode 100644
index 000000000..9b730adcd
--- /dev/null
+++ b/drm/1.2/vts/functional/vendor_modules.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef VENDOR_MODULES_H
+#define VENDOR_MODULES_H
+
+#include <map>
+#include <vector>
+#include <string>
+
+#include <SharedLibrary.h>
+
+using ::android::hardware::drm::V1_0::helper::SharedLibrary;
+
+class DrmHalVTSVendorModule;
+
+namespace drm_vts {
+class VendorModules {
+ public:
+ /**
+ * Initialize with a file system path where the shared libraries
+ * are to be found.
+ */
+ explicit VendorModules(const std::string& dir) {
+ scanModules(dir);
+ }
+ ~VendorModules() {}
+
+ /**
+ * Retrieve a DrmHalVTSVendorModule given its full path. The
+ * getAPIVersion method can be used to determine the versioned
+ * subclass type.
+ */
+ DrmHalVTSVendorModule* getModule(const std::string& path);
+
+ /**
+ * Return the list of paths to available vendor modules.
+ */
+ std::vector<std::string> getPathList() const {return mPathList;}
+
+ private:
+ std::vector<std::string> mPathList;
+ std::map<std::string, std::unique_ptr<SharedLibrary>> mOpenLibraries;
+
+ /**
+ * Scan the list of paths to available vendor modules.
+ */
+ void scanModules(const std::string& dir);
+
+ inline bool endsWith(const std::string& str, const std::string& suffix) const {
+ if (suffix.size() > str.size()) return false;
+ return std::equal(suffix.rbegin(), suffix.rend(), str.rbegin());
+ }
+
+ VendorModules(const VendorModules&) = delete;
+ void operator=(const VendorModules&) = delete;
+};
+};
+
+#endif // VENDOR_MODULES_H