Add VTS tests for drm+crypto HALs
Tests: drm vts tests are passing
related-to-bug: 32815560
Change-Id: I2b36f27fbb42eba37f3e5a26acea0e359e60b3af
diff --git a/drm/1.0/vts/doc/Drm_Vendor_Modules_v1.pdf b/drm/1.0/vts/doc/Drm_Vendor_Modules_v1.pdf
new file mode 100644
index 0000000..1b44e4f
--- /dev/null
+++ b/drm/1.0/vts/doc/Drm_Vendor_Modules_v1.pdf
Binary files differ
diff --git a/drm/1.0/vts/functional/Android.bp b/drm/1.0/vts/functional/Android.bp
new file mode 100644
index 0000000..546aa12
--- /dev/null
+++ b/drm/1.0/vts/functional/Android.bp
@@ -0,0 +1,46 @@
+//
+// Copyright (C) 2017 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: "drm_hidl_test",
+ srcs: [
+ "drm_hal_clearkey_test.cpp",
+ "drm_hal_vendor_test.cpp",
+ "shared_library.cpp",
+ "vendor_modules.cpp"
+ ],
+ shared_libs: [
+ "android.hardware.drm@1.0",
+ "android.hidl.allocator@1.0",
+ "android.hidl.memory@1.0",
+ "libbase",
+ "libcutils",
+ "libhidlbase",
+ "libhidlmemory",
+ "libhidltransport",
+ "libhwbinder",
+ "liblog",
+ "libnativehelper",
+ "libutils",
+ ],
+ static_libs: [
+ "VtsHalHidlTargetTestBase"
+ ],
+ cflags: [
+ "-O0",
+ "-g",
+ ],
+}
diff --git a/drm/1.0/vts/functional/drm_hal_clearkey_test.cpp b/drm/1.0/vts/functional/drm_hal_clearkey_test.cpp
new file mode 100644
index 0000000..2296d2d
--- /dev/null
+++ b/drm/1.0/vts/functional/drm_hal_clearkey_test.cpp
@@ -0,0 +1,904 @@
+/*
+ * Copyright (C) 2017 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_test@1.0"
+
+#include <android-base/logging.h>
+#include <android/hardware/drm/1.0/ICryptoFactory.h>
+#include <android/hardware/drm/1.0/ICryptoPlugin.h>
+#include <android/hardware/drm/1.0/IDrmFactory.h>
+#include <android/hardware/drm/1.0/IDrmPlugin.h>
+#include <android/hardware/drm/1.0/types.h>
+#include <android/hidl/allocator/1.0/IAllocator.h>
+#include <gtest/gtest.h>
+#include <hidl/HidlSupport.h>
+#include <hidlmemory/mapping.h>
+#include <memory>
+#include <random>
+
+#include "VtsHalHidlTargetTestBase.h"
+
+using ::android::hardware::drm::V1_0::BufferType;
+using ::android::hardware::drm::V1_0::DestinationBuffer;
+using ::android::hardware::drm::V1_0::ICryptoFactory;
+using ::android::hardware::drm::V1_0::ICryptoPlugin;
+using ::android::hardware::drm::V1_0::IDrmFactory;
+using ::android::hardware::drm::V1_0::IDrmPlugin;
+using ::android::hardware::drm::V1_0::KeyedVector;
+using ::android::hardware::drm::V1_0::KeyValue;
+using ::android::hardware::drm::V1_0::KeyRequestType;
+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::SecureStop;
+using ::android::hardware::drm::V1_0::SecureStopId;
+using ::android::hardware::drm::V1_0::SessionId;
+using ::android::hardware::drm::V1_0::SharedBuffer;
+using ::android::hardware::drm::V1_0::Status;
+using ::android::hardware::drm::V1_0::SubSample;
+
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hidl::allocator::V1_0::IAllocator;
+using ::android::hidl::memory::V1_0::IMemory;
+using ::android::sp;
+
+using std::string;
+using std::unique_ptr;
+using std::random_device;
+using std::map;
+using std::mt19937;
+using std::vector;
+
+/**
+ * These clearkey tests use white box knowledge of the legacy clearkey
+ * plugin to verify that the HIDL HAL services and interfaces are working.
+ * It is not intended to verify any vendor's HAL implementation. If you
+ * are looking for vendor HAL tests, see drm_hal_vendor_test.cpp
+ */
+#define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk())
+#define EXPECT_OK(ret) EXPECT_TRUE(ret.isOk())
+
+static const uint8_t kClearKeyUUID[16] = {
+ 0x10, 0x77, 0xEF, 0xEC, 0xC0, 0xB2, 0x4D, 0x02,
+ 0xAC, 0xE3, 0x3C, 0x1E, 0x52, 0xE2, 0xFB, 0x4B};
+
+static const uint8_t kInvalidUUID[16] = {
+ 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
+ 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80};
+
+class DrmHalClearkeyFactoryTest : public ::testing::VtsHalHidlTargetTestBase {
+ public:
+ virtual void SetUp() override {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ ALOGD("Running test %s.%s", test_info->test_case_name(),
+ test_info->name());
+
+ drmFactory =
+ ::testing::VtsHalHidlTargetTestBase::getService<IDrmFactory>(
+ "drm");
+ ASSERT_NE(drmFactory, nullptr);
+ cryptoFactory =
+ ::testing::VtsHalHidlTargetTestBase::getService<ICryptoFactory>(
+ "crypto");
+ ASSERT_NE(cryptoFactory, nullptr);
+ }
+
+ virtual void TearDown() override {}
+
+ protected:
+ sp<IDrmFactory> drmFactory;
+ sp<ICryptoFactory> cryptoFactory;
+};
+
+/**
+ * Ensure the factory supports the clearkey scheme UUID
+ */
+TEST_F(DrmHalClearkeyFactoryTest, ClearKeyPluginSupported) {
+ EXPECT_TRUE(drmFactory->isCryptoSchemeSupported(kClearKeyUUID));
+ EXPECT_TRUE(cryptoFactory->isCryptoSchemeSupported(kClearKeyUUID));
+}
+
+/**
+ * Ensure the factory doesn't support an invalid scheme UUID
+ */
+TEST_F(DrmHalClearkeyFactoryTest, InvalidPluginNotSupported) {
+ EXPECT_FALSE(drmFactory->isCryptoSchemeSupported(kInvalidUUID));
+ EXPECT_FALSE(cryptoFactory->isCryptoSchemeSupported(kInvalidUUID));
+}
+
+/**
+ * Ensure clearkey drm plugin can be created
+ */
+TEST_F(DrmHalClearkeyFactoryTest, CreateClearKeyDrmPlugin) {
+ hidl_string packageName("android.hardware.drm.test");
+ auto res = drmFactory->createPlugin(
+ kClearKeyUUID, packageName,
+ [&](Status status, const sp<IDrmPlugin>& plugin) {
+ EXPECT_EQ(Status::OK, status);
+ EXPECT_NE(plugin, nullptr);
+ });
+ EXPECT_OK(res);
+}
+
+/**
+ * Ensure clearkey crypto plugin can be created
+ */
+TEST_F(DrmHalClearkeyFactoryTest, CreateClearKeyCryptoPlugin) {
+ hidl_vec<uint8_t> initVec;
+ auto res = cryptoFactory->createPlugin(
+ kClearKeyUUID, initVec,
+ [&](Status status, const sp<ICryptoPlugin>& plugin) {
+ EXPECT_EQ(Status::OK, status);
+ EXPECT_NE(plugin, nullptr);
+ });
+ EXPECT_OK(res);
+}
+
+/**
+ * Ensure invalid drm plugin can't be created
+ */
+TEST_F(DrmHalClearkeyFactoryTest, CreateInvalidDrmPlugin) {
+ hidl_string packageName("android.hardware.drm.test");
+ auto res = drmFactory->createPlugin(
+ kInvalidUUID, packageName,
+ [&](Status status, const sp<IDrmPlugin>& plugin) {
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+ EXPECT_EQ(plugin, nullptr);
+ });
+ EXPECT_OK(res);
+}
+
+/**
+ * Ensure invalid crypto plugin can't be created
+ */
+TEST_F(DrmHalClearkeyFactoryTest, CreateInvalidCryptoPlugin) {
+ hidl_vec<uint8_t> initVec;
+ auto res = cryptoFactory->createPlugin(
+ kInvalidUUID, initVec,
+ [&](Status status, const sp<ICryptoPlugin>& plugin) {
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+ EXPECT_EQ(plugin, nullptr);
+ });
+ EXPECT_OK(res);
+}
+
+class DrmHalClearkeyPluginTest : public DrmHalClearkeyFactoryTest {
+ public:
+ virtual void SetUp() override {
+ // Create factories
+ DrmHalClearkeyFactoryTest::SetUp();
+
+ ASSERT_NE(drmFactory, nullptr);
+ hidl_string packageName("android.hardware.drm.test");
+ auto res = drmFactory->createPlugin(
+ kClearKeyUUID, packageName,
+ [this](Status status, const sp<IDrmPlugin>& plugin) {
+ EXPECT_EQ(Status::OK, status);
+ ASSERT_NE(plugin, nullptr);
+ drmPlugin = plugin;
+ });
+ ASSERT_OK(res);
+
+ hidl_vec<uint8_t> initVec;
+ res = cryptoFactory->createPlugin(
+ kClearKeyUUID, initVec,
+ [this](Status status, const sp<ICryptoPlugin>& plugin) {
+ EXPECT_EQ(Status::OK, status);
+ ASSERT_NE(plugin, nullptr);
+ cryptoPlugin = plugin;
+ });
+ ASSERT_OK(res);
+ }
+
+ virtual void TearDown() override {}
+
+ SessionId openSession();
+ void closeSession(const SessionId& sessionId);
+ sp<IMemory> getDecryptMemory(size_t size, size_t index);
+
+ protected:
+ sp<IDrmPlugin> drmPlugin;
+ sp<ICryptoPlugin> cryptoPlugin;
+};
+
+/**
+ * DrmPlugin tests
+ */
+
+/**
+ * Test that the plugin can return a provision request. Since
+ * the clearkey plugin doesn't support provisioning, it is
+ * expected to return Status::ERROR_DRM_CANNOT_HANDLE.
+ */
+TEST_F(DrmHalClearkeyPluginTest, GetProvisionRequest) {
+ hidl_string certificateType;
+ hidl_string certificateAuthority;
+ auto res = drmPlugin->getProvisionRequest(
+ certificateType, certificateAuthority,
+ [&](Status status, const hidl_vec<uint8_t>&, const hidl_string&) {
+ // clearkey doesn't require provisioning
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+ });
+ EXPECT_OK(res);
+}
+
+/**
+ * The DRM HAL should return BAD_VALUE if an empty provisioning
+ * response is provided.
+ */
+TEST_F(DrmHalClearkeyPluginTest, ProvideEmptyProvisionResponse) {
+ hidl_vec<uint8_t> response;
+ auto res = drmPlugin->provideProvisionResponse(
+ response, [&](Status status, const hidl_vec<uint8_t>&,
+ const hidl_vec<uint8_t>&) {
+ EXPECT_EQ(Status::BAD_VALUE, status);
+ });
+ EXPECT_OK(res);
+}
+
+/**
+ * Helper method to open a session and verify that a non-empty
+ * session ID is returned
+ */
+SessionId DrmHalClearkeyPluginTest::openSession() {
+ SessionId sessionId;
+
+ auto res = drmPlugin->openSession(
+ [&sessionId](Status status, const SessionId& id) {
+ EXPECT_EQ(Status::OK, status);
+ EXPECT_NE(0u, id.size());
+ sessionId = id;
+ });
+ EXPECT_OK(res);
+ return sessionId;
+}
+
+/**
+ * Helper method to close a session
+ */
+void DrmHalClearkeyPluginTest::closeSession(const SessionId& sessionId) {
+ auto result = drmPlugin->closeSession(sessionId);
+ EXPECT_EQ(Status::OK, result);
+}
+
+/**
+ * Test that a session can be opened and closed
+ */
+TEST_F(DrmHalClearkeyPluginTest, OpenCloseSession) {
+ auto sessionId = openSession();
+ closeSession(sessionId);
+}
+
+/**
+ * Test that attempting to close an invalid (empty) sessionId
+ * is prohibited with the documented error code.
+ */
+TEST_F(DrmHalClearkeyPluginTest, CloseInvalidSession) {
+ SessionId invalidSessionId;
+ Status result = drmPlugin->closeSession(invalidSessionId);
+ EXPECT_EQ(Status::BAD_VALUE, result);
+}
+
+/**
+ * Test that attempting to close a session that is already closed
+ * is prohibited with the documented error code.
+ */
+TEST_F(DrmHalClearkeyPluginTest, CloseClosedSession) {
+ SessionId sessionId = openSession();
+ closeSession(sessionId);
+ Status result = drmPlugin->closeSession(sessionId);
+ EXPECT_EQ(Status::ERROR_DRM_SESSION_NOT_OPENED, result);
+}
+
+/**
+ * A get key request should fail if no sessionId is provided
+ */
+TEST_F(DrmHalClearkeyPluginTest, GetKeyRequestNoSession) {
+ SessionId invalidSessionId;
+ hidl_vec<uint8_t> initData;
+ hidl_string mimeType = "video/mp4";
+ KeyedVector optionalParameters;
+ auto res = drmPlugin->getKeyRequest(
+ invalidSessionId, initData, mimeType, KeyType::STREAMING,
+ optionalParameters,
+ [&](Status status, const hidl_vec<uint8_t>&, KeyRequestType,
+ const hidl_string&) { EXPECT_EQ(Status::BAD_VALUE, status); });
+ EXPECT_OK(res);
+}
+
+/**
+ * The clearkey plugin doesn't support offline key requests.
+ * Test that the plugin returns the expected error code in
+ * this case.
+ */
+TEST_F(DrmHalClearkeyPluginTest, GetKeyRequestOfflineKeyTypeNotSupported) {
+ auto sessionId = openSession();
+ hidl_vec<uint8_t> initData;
+ hidl_string mimeType = "video/mp4";
+ KeyedVector optionalParameters;
+
+ auto res = drmPlugin->getKeyRequest(
+ sessionId, initData, mimeType, KeyType::OFFLINE, optionalParameters,
+ [&](Status status, const hidl_vec<uint8_t>&, KeyRequestType,
+ const hidl_string&) {
+ // Clearkey plugin doesn't support offline key type
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+ });
+ EXPECT_OK(res);
+ closeSession(sessionId);
+}
+
+/**
+ * Test that the plugin returns the documented error for the
+ * case of attempting to generate a key request using an
+ * invalid mime type
+ */
+TEST_F(DrmHalClearkeyPluginTest, GetKeyRequestBadMime) {
+ auto sessionId = openSession();
+ hidl_vec<uint8_t> initData;
+ hidl_string mimeType = "video/unknown";
+ KeyedVector optionalParameters;
+ auto res = drmPlugin->getKeyRequest(
+ sessionId, initData, mimeType, KeyType::STREAMING,
+ optionalParameters, [&](Status status, const hidl_vec<uint8_t>&,
+ KeyRequestType, const hidl_string&) {
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+ });
+ EXPECT_OK(res);
+ closeSession(sessionId);
+}
+
+/**
+ * Test that a closed sessionID returns SESSION_NOT_OPENED
+ */
+TEST_F(DrmHalClearkeyPluginTest, ProvideKeyResponseClosedSession) {
+ SessionId session = openSession();
+ closeSession(session);
+
+ hidl_vec<uint8_t> keyResponse = {0x7b, 0x22, 0x6b, 0x65,
+ 0x79, 0x73, 0x22, 0x3a};
+ auto res = drmPlugin->provideKeyResponse(
+ session, keyResponse,
+ [&](Status status, const hidl_vec<uint8_t>& keySetId) {
+ EXPECT_EQ(Status::ERROR_DRM_SESSION_NOT_OPENED, status);
+ EXPECT_EQ(0u, keySetId.size());
+ });
+ EXPECT_OK(res);
+}
+
+/**
+ * Test that an empty sessionID returns BAD_VALUE
+ */
+TEST_F(DrmHalClearkeyPluginTest, ProvideKeyResponseInvalidSessionId) {
+ SessionId session;
+
+ hidl_vec<uint8_t> keyResponse = {0x7b, 0x22, 0x6b, 0x65,
+ 0x79, 0x73, 0x22, 0x3a};
+ auto res = drmPlugin->provideKeyResponse(
+ session, keyResponse,
+ [&](Status status, const hidl_vec<uint8_t>& keySetId) {
+ EXPECT_EQ(Status::BAD_VALUE, status);
+ EXPECT_EQ(0u, keySetId.size());
+ });
+ EXPECT_OK(res);
+}
+
+/**
+ * Test that an empty key response returns BAD_VALUE
+ */
+TEST_F(DrmHalClearkeyPluginTest, ProvideKeyResponseEmptyResponse) {
+ SessionId session = openSession();
+ hidl_vec<uint8_t> emptyResponse;
+ auto res = drmPlugin->provideKeyResponse(
+ session, emptyResponse,
+ [&](Status status, const hidl_vec<uint8_t>& keySetId) {
+ EXPECT_EQ(Status::BAD_VALUE, status);
+ EXPECT_EQ(0u, keySetId.size());
+ });
+ EXPECT_OK(res);
+ closeSession(session);
+}
+
+/**
+ * Test that the clearkey plugin doesn't support getting
+ * secure stops.
+ */
+TEST_F(DrmHalClearkeyPluginTest, GetSecureStops) {
+ auto res = drmPlugin->getSecureStops(
+ [&](Status status, const hidl_vec<SecureStop>&) {
+ // Clearkey plugin doesn't support secure stops
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+ });
+ EXPECT_OK(res);
+}
+
+/**
+ * Test that the clearkey plugin returns BAD_VALUE if
+ * an empty ssid is provided.
+ */
+TEST_F(DrmHalClearkeyPluginTest, GetSecureStopEmptySSID) {
+ SecureStopId ssid;
+ auto res = drmPlugin->getSecureStop(
+ ssid, [&](Status status, const SecureStop&) {
+ EXPECT_EQ(Status::BAD_VALUE, status);
+ });
+ EXPECT_OK(res);
+}
+
+/**
+ * Test that releasing all secure stops isn't handled by
+ * clearkey.
+ */
+TEST_F(DrmHalClearkeyPluginTest, ReleaseAllSecureStops) {
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE,
+ drmPlugin->releaseAllSecureStops());
+}
+
+/**
+ * Test that releasing a specific secure stop with an empty
+ * SSID returns BAD_VALUE.
+ */
+TEST_F(DrmHalClearkeyPluginTest, ReleaseSecureStopEmptySSID) {
+ SecureStopId ssid;
+ Status status = drmPlugin->releaseSecureStop(ssid);
+ EXPECT_EQ(Status::BAD_VALUE, status);
+}
+
+/**
+ * The following four tests verify that the properties
+ * defined in the MediaDrm API are supported by
+ * the plugin.
+ */
+TEST_F(DrmHalClearkeyPluginTest, GetVendorProperty) {
+ auto res = drmPlugin->getPropertyString(
+ "vendor", [&](Status status, const hidl_string& value) {
+ EXPECT_EQ(Status::OK, status);
+ EXPECT_EQ("Google", value);
+ });
+ EXPECT_OK(res);
+}
+
+TEST_F(DrmHalClearkeyPluginTest, GetVersionProperty) {
+ auto res = drmPlugin->getPropertyString(
+ "version", [&](Status status, const hidl_string& value) {
+ EXPECT_EQ(Status::OK, status);
+ EXPECT_EQ("1.0", value);
+ });
+ EXPECT_OK(res);
+}
+
+TEST_F(DrmHalClearkeyPluginTest, GetDescriptionProperty) {
+ auto res = drmPlugin->getPropertyString(
+ "description", [&](Status status, const hidl_string& value) {
+ EXPECT_EQ(Status::OK, status);
+ EXPECT_EQ("ClearKey CDM", value);
+ });
+ EXPECT_OK(res);
+}
+
+TEST_F(DrmHalClearkeyPluginTest, GetAlgorithmsProperty) {
+ auto res = drmPlugin->getPropertyString(
+ "algorithms", [&](Status status, const hidl_string& value) {
+ EXPECT_EQ(Status::OK, status);
+ EXPECT_EQ("", value);
+ });
+ EXPECT_OK(res);
+}
+
+/**
+ * Test that attempting to read invalid string and byte array
+ * properties returns the documented error code.
+ */
+TEST_F(DrmHalClearkeyPluginTest, GetInvalidStringProperty) {
+ auto res = drmPlugin->getPropertyString(
+ "invalid", [&](Status status, const hidl_string&) {
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+ });
+ EXPECT_OK(res);
+}
+
+TEST_F(DrmHalClearkeyPluginTest, GetByteArrayPropertyNotSupported) {
+ auto res = drmPlugin->getPropertyByteArray(
+ "deviceUniqueId", [&](Status status, const hidl_vec<uint8_t>&) {
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+ });
+ EXPECT_OK(res);
+}
+
+/**
+ * Clearkey doesn't support setting string or byte array properties,
+ * particularly an undefined one.
+ */
+TEST_F(DrmHalClearkeyPluginTest, SetStringPropertyNotSupported) {
+ Status status = drmPlugin->setPropertyString("property", "value");
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+}
+
+TEST_F(DrmHalClearkeyPluginTest, SetByteArrayPropertyNotSupported) {
+ hidl_vec<uint8_t> value;
+ Status status = drmPlugin->setPropertyByteArray("property", value);
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+}
+
+/**
+ * Clearkey doesn't support setting cipher algorithms, verify it
+ */
+TEST_F(DrmHalClearkeyPluginTest, SetCipherAlgorithmNotSupported) {
+ SessionId session = openSession();
+ hidl_string algorithm = "AES/CBC/NoPadding";
+ Status status = drmPlugin->setCipherAlgorithm(session, algorithm);
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+ closeSession(session);
+}
+
+/**
+ * Setting an empty algorithm should return BAD_VALUE
+ */
+TEST_F(DrmHalClearkeyPluginTest, SetCipherEmptyAlgorithm) {
+ SessionId session = openSession();
+ hidl_string algorithm;
+ Status status = drmPlugin->setCipherAlgorithm(session, algorithm);
+ EXPECT_EQ(Status::BAD_VALUE, status);
+ closeSession(session);
+}
+
+/**
+ * Setting a cipher algorithm with no session returns BAD_VALUE
+ */
+TEST_F(DrmHalClearkeyPluginTest, SetCipherAlgorithmNoSession) {
+ SessionId session;
+ hidl_string algorithm = "AES/CBC/NoPadding";
+ Status status = drmPlugin->setCipherAlgorithm(session, algorithm);
+ EXPECT_EQ(Status::BAD_VALUE, status);
+}
+
+/**
+ * Clearkey doesn't support setting mac algorithms, verify it
+ */
+TEST_F(DrmHalClearkeyPluginTest, SetMacAlgorithmNotSupported) {
+ SessionId session = openSession();
+ hidl_string algorithm = "HmacSHA256";
+ Status status = drmPlugin->setMacAlgorithm(session, algorithm);
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+ closeSession(session);
+}
+
+/**
+ * Setting an empty algorithm should return BAD_VALUE
+ */
+TEST_F(DrmHalClearkeyPluginTest, SetMacEmptyAlgorithm) {
+ SessionId session = openSession();
+ hidl_string algorithm;
+ Status status = drmPlugin->setMacAlgorithm(session, algorithm);
+ EXPECT_EQ(Status::BAD_VALUE, status);
+ closeSession(session);
+}
+
+/**
+ * Setting a mac algorithm with no session should return BAD_VALUE
+ */
+TEST_F(DrmHalClearkeyPluginTest, SetMacAlgorithmNoSession) {
+ SessionId session;
+ hidl_string algorithm = "HmacSHA256";
+ Status status = drmPlugin->setMacAlgorithm(session, algorithm);
+ EXPECT_EQ(Status::BAD_VALUE, status);
+}
+
+/**
+ * The Generic* methods provide general purpose crypto operations
+ * that may be used for applications other than DRM. They leverage
+ * the hardware root of trust and secure key distribution mechanisms
+ * of a DRM system to enable app-specific crypto functionality where
+ * the crypto keys are not exposed outside of the trusted execution
+ * environment.
+ *
+ * Clearkey doesn't support generic encrypt/decrypt/sign/verify.
+ */
+TEST_F(DrmHalClearkeyPluginTest, GenericEncryptNotSupported) {
+ SessionId session = openSession();
+ ;
+ hidl_vec<uint8_t> keyId = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
+ hidl_vec<uint8_t> input = {1, 2, 3, 4, 5};
+ hidl_vec<uint8_t> iv = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ auto res = drmPlugin->encrypt(session, keyId, input, iv,
+ [&](Status status, const hidl_vec<uint8_t>&) {
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE,
+ status);
+ });
+ EXPECT_OK(res);
+ closeSession(session);
+}
+
+TEST_F(DrmHalClearkeyPluginTest, GenericDecryptNotSupported) {
+ SessionId session = openSession();
+ ;
+ hidl_vec<uint8_t> keyId = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
+ hidl_vec<uint8_t> input = {1, 2, 3, 4, 5};
+ hidl_vec<uint8_t> iv = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ auto res = drmPlugin->decrypt(session, keyId, input, iv,
+ [&](Status status, const hidl_vec<uint8_t>&) {
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE,
+ status);
+ });
+ EXPECT_OK(res);
+ closeSession(session);
+}
+
+TEST_F(DrmHalClearkeyPluginTest, GenericSignNotSupported) {
+ SessionId session = openSession();
+ ;
+ hidl_vec<uint8_t> keyId = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
+ hidl_vec<uint8_t> message = {1, 2, 3, 4, 5};
+ auto res = drmPlugin->sign(session, keyId, message,
+ [&](Status status, const hidl_vec<uint8_t>&) {
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE,
+ status);
+ });
+ EXPECT_OK(res);
+ closeSession(session);
+}
+
+TEST_F(DrmHalClearkeyPluginTest, GenericVerifyNotSupported) {
+ SessionId session = openSession();
+ ;
+ hidl_vec<uint8_t> keyId = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
+ hidl_vec<uint8_t> message = {1, 2, 3, 4, 5};
+ hidl_vec<uint8_t> signature = {0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0};
+ auto res = drmPlugin->verify(
+ session, keyId, message, signature, [&](Status status, bool) {
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+ });
+ EXPECT_OK(res);
+ closeSession(session);
+}
+
+TEST_F(DrmHalClearkeyPluginTest, GenericSignRSANotSupported) {
+ SessionId session = openSession();
+ hidl_string algorithm = "RSASSA-PSS-SHA1";
+ hidl_vec<uint8_t> message = {1, 2, 3, 4, 5};
+ hidl_vec<uint8_t> wrappedKey = {0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0};
+ auto res = drmPlugin->signRSA(session, algorithm, message, wrappedKey,
+ [&](Status status, const hidl_vec<uint8_t>&) {
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE,
+ status);
+ });
+ EXPECT_OK(res);
+ closeSession(session);
+}
+
+/**
+ * CryptoPlugin tests
+ */
+
+/**
+ * Clearkey doesn't support secure decoder and is expected to
+ * return false.
+ */
+TEST_F(DrmHalClearkeyPluginTest, RequiresSecureDecoder) {
+ EXPECT_FALSE(cryptoPlugin->requiresSecureDecoderComponent("cenc"));
+}
+
+/**
+ * Verify that requiresSecureDecoderComponent handles empty mimetype
+ */
+TEST_F(DrmHalClearkeyPluginTest, RequiresSecureDecoderEmptyMimeType) {
+ EXPECT_FALSE(cryptoPlugin->requiresSecureDecoderComponent(""));
+}
+
+/**
+ * Exercise the NotifyResolution API. There is no observable result,
+ * just call the method for coverage.
+ */
+TEST_F(DrmHalClearkeyPluginTest, NotifyResolution) {
+ cryptoPlugin->notifyResolution(1920, 1080);
+}
+
+/**
+ * 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> DrmHalClearkeyPluginTest::getDecryptMemory(size_t size,
+ size_t index) {
+ sp<IAllocator> ashmemAllocator = IAllocator::getService("ashmem");
+ EXPECT_NE(ashmemAllocator, nullptr);
+
+ hidl_memory hidlMemory;
+ auto res = ashmemAllocator->allocate(
+ size, [&](bool success, const hidl_memory& memory) {
+ EXPECT_EQ(true, success);
+ EXPECT_OK(cryptoPlugin->setSharedBufferBase(memory, index));
+ hidlMemory = memory;
+ });
+ EXPECT_OK(res);
+
+ sp<IMemory> mappedMemory = mapMemory(hidlMemory);
+ EXPECT_OK(cryptoPlugin->setSharedBufferBase(hidlMemory, index));
+ return mappedMemory;
+}
+
+/**
+ * Exercise the setMediaDrmSession method. setMediaDrmSession
+ * is used to associate a drm session with a crypto session.
+ */
+TEST_F(DrmHalClearkeyPluginTest, SetMediaDrmSession) {
+ auto sessionId = openSession();
+ Status status = cryptoPlugin->setMediaDrmSession(sessionId);
+ EXPECT_EQ(Status::OK, status);
+ closeSession(sessionId);
+}
+
+/**
+ * setMediaDrmSession with a closed session id
+ */
+TEST_F(DrmHalClearkeyPluginTest, SetMediaDrmSessionClosedSession) {
+ auto sessionId = openSession();
+ closeSession(sessionId);
+ Status status = cryptoPlugin->setMediaDrmSession(sessionId);
+ EXPECT_EQ(Status::ERROR_DRM_SESSION_NOT_OPENED, status);
+}
+
+/**
+ * Decrypt tests
+ */
+
+class DrmHalClearkeyDecryptTest : public DrmHalClearkeyPluginTest {
+ public:
+ void loadKeys(const SessionId& sessionId);
+ void fillRandom(const sp<IMemory>& memory);
+ hidl_array<uint8_t, 16> toHidlArray(const vector<uint8_t>& vec) {
+ EXPECT_EQ(vec.size(), 16u);
+ return hidl_array<uint8_t, 16>(&vec[0]);
+ }
+};
+
+/**
+ * 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.
+ */
+void DrmHalClearkeyDecryptTest::loadKeys(const SessionId& sessionId) {
+ hidl_vec<uint8_t> 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};
+
+ hidl_vec<uint8_t> expectedKeyRequest = {
+ 0x7b, 0x22, 0x6b, 0x69, 0x64, 0x73, 0x22, 0x3a, 0x5b, 0x22, 0x59,
+ 0x41, 0x59, 0x65, 0x41, 0x58, 0x35, 0x48, 0x66, 0x6f, 0x64, 0x2b,
+ 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};
+
+ hidl_vec<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, 0x2b, 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, 0x0a};
+
+ hidl_string mimeType = "video/mp4";
+ KeyedVector optionalParameters;
+ auto res = drmPlugin->getKeyRequest(
+ sessionId, initData, mimeType, KeyType::STREAMING,
+ optionalParameters,
+ [&](Status status, const hidl_vec<uint8_t>& request,
+ KeyRequestType requestType, const hidl_string&) {
+ EXPECT_EQ(Status::OK, status);
+ EXPECT_EQ(KeyRequestType::INITIAL, requestType);
+ EXPECT_EQ(request, expectedKeyRequest);
+ });
+ EXPECT_OK(res);
+
+ res = drmPlugin->provideKeyResponse(
+ sessionId, knownKeyResponse,
+ [&](Status status, const hidl_vec<uint8_t>& keySetId) {
+ EXPECT_EQ(Status::OK, status);
+ EXPECT_EQ(0u, keySetId.size());
+ });
+ EXPECT_OK(res);
+}
+
+void DrmHalClearkeyDecryptTest::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();
+ }
+}
+
+/**
+ * Positive decrypt test. "Decrypt" a single clear
+ * segment. Verify data matches.
+ */
+TEST_F(DrmHalClearkeyDecryptTest, ClearSegmentTest) {
+ const size_t kSegmentSize = 1024;
+ const size_t kSegmentIndex = 0;
+ const vector<uint8_t> keyId = {0x60, 0x06, 0x1e, 0x01, 0x7e, 0x47,
+ 0x7e, 0x87, 0x7e, 0x57, 0xd0, 0x0d,
+ 0x1e, 0xd0, 0x0d, 0x1e};
+ uint8_t iv[16] = {0};
+
+ sp<IMemory> sharedMemory =
+ getDecryptMemory(kSegmentSize * 2, kSegmentIndex);
+
+ SharedBuffer sourceBuffer = {
+ .bufferId = kSegmentIndex, .offset = 0, .size = kSegmentSize};
+ fillRandom(sharedMemory);
+
+ DestinationBuffer destBuffer = {.type = BufferType::SHARED_MEMORY,
+ {.bufferId = kSegmentIndex,
+ .offset = kSegmentSize,
+ .size = kSegmentSize},
+ .secureMemory = nullptr};
+
+ Pattern noPattern = {0, 0};
+ vector<SubSample> subSamples = {{.numBytesOfClearData = kSegmentSize,
+ .numBytesOfEncryptedData = 0}};
+ uint64_t offset = 0;
+
+ auto sessionId = openSession();
+ loadKeys(sessionId);
+
+ Status status = cryptoPlugin->setMediaDrmSession(sessionId);
+ EXPECT_EQ(Status::OK, status);
+
+ const bool kNotSecure = false;
+ auto res = cryptoPlugin->decrypt(
+ kNotSecure, toHidlArray(keyId), iv, Mode::UNENCRYPTED, noPattern,
+ subSamples, sourceBuffer, offset, destBuffer,
+ [&](Status status, uint32_t bytesWritten, string detailedError) {
+ EXPECT_EQ(Status::OK, status) << "Failure in decryption:"
+ << detailedError;
+ EXPECT_EQ(bytesWritten, kSegmentSize);
+ });
+ EXPECT_OK(res);
+
+ uint8_t* base = static_cast<uint8_t*>(
+ static_cast<void*>(sharedMemory->getPointer()));
+
+ EXPECT_EQ(0, memcmp(static_cast<void*>(base),
+ static_cast<void*>(base + kSegmentSize), kSegmentSize))
+ << "decrypt data mismatch";
+ closeSession(sessionId);
+}
diff --git a/drm/1.0/vts/functional/drm_hal_vendor_module_api.h b/drm/1.0/vts/functional/drm_hal_vendor_module_api.h
new file mode 100644
index 0000000..db19719
--- /dev/null
+++ b/drm/1.0/vts/functional/drm_hal_vendor_module_api.h
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2017 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_VENDOR_MODULE_API_H
+#define DRM_HAL_VENDOR_MODULE_API_H
+
+#include <stdint.h>
+#include <map>
+#include <string>
+#include <vector>
+
+/**
+ * The DRM and Crypto HALs interact with vendor-provided HAL implementations
+ * that have DRM-specific capabilities. Since the VTS tests cannot contain
+ * DRM-specific functionality, supporting modules are required to enable VTS
+ * to validate HAL implementations in a generic way. If the vendor-specific
+ * VTS module is not provided for a given drm HAL implementation, only very
+ * small subset of functionality can be verified.
+ *
+ * As an example, a DRM HAL implementation interacts with a DRM-specific
+ * license server to obtain licenses for decrypting content. The DRM HAL
+ * implementation generates a key request message, delivers it to the server
+ * and receives a key response message which is then loaded into the HAL. Once
+ * the keys are loaded, the Crypto HAL decryption functionality and performance
+ * and other associated APIs can be tested by the common VTS test suite.
+ *
+ * Vendor-specific VTS modules are shared libraries used by the DRM VTS test.
+ * They provide a set of functions to support VTS testing of the DRM HAL module.
+ *
+ * The modules are placed in a common location on the file system. The VTS test
+ * scans through all vendor-provided support libraries and runs the VTS test
+ * suite on each library that is found.
+ *
+ * The vendor-specific module exposes an extern “C” vendorModuleFactory()
+ * function that returns a DrmHalVTSVendorModule instance. DrmHalVTSVendorModule
+ * instances are versioned, where each version is represented by subclass of
+ * DrmHalVTSVendorModule that corresponds to the API version. For example, a
+ * vendor-specific module that implements version 1 of the API would return a
+ * DrmHalVTSVendorModule_V1 from the vendorModuleFactory() function.
+ */
+
+class DrmHalVTSVendorModule;
+
+extern "C" {
+/**
+ * The factory method for creating DrmHalVTSVendorModule instances. The returned
+ * instance will be a subclass of DrmHalVTSVendorModule that corresponds to the
+ * supported API version.
+ */
+DrmHalVTSVendorModule* vendorModuleFactory();
+};
+
+class DrmHalVTSVendorModule {
+ public:
+ DrmHalVTSVendorModule() {}
+ virtual ~DrmHalVTSVendorModule() {}
+
+ /**
+ * Return the vendor-specific module API version. The version is an integer
+ * value with initial version 1. The API version indicates which subclass
+ * version DrmHalVTSVendorModule this instance is.
+ */
+ virtual uint32_t getAPIVersion() = 0;
+
+ /**
+ * Return the UUID for the DRM HAL implementation. Protection System
+ * Specific
+ * UUID (see http://dashif.org/identifiers/protection/)
+ */
+ virtual std::vector<uint8_t> getUUID() = 0;
+
+ /**
+ * Return the service name for the DRM HAL implementation. If the hal is a
+ * legacy
+ * drm plugin, i.e. not running as a HIDL service, return the empty string.
+ */
+ virtual std::string getServiceName() = 0;
+
+ private:
+ DrmHalVTSVendorModule(const DrmHalVTSVendorModule&) = delete;
+ void operator=(const DrmHalVTSVendorModule&) = delete;
+};
+
+/**
+ * API Version 1. This is the baseline version that supports a minimal set
+ * of VTS tests.
+ */
+class DrmHalVTSVendorModule_V1 : public DrmHalVTSVendorModule {
+ public:
+ DrmHalVTSVendorModule_V1() {}
+ virtual ~DrmHalVTSVendorModule_V1() {}
+
+ virtual uint32_t getAPIVersion() { return 1; }
+
+ /**
+ * Handle a provisioning request. This function will be called if the HAL
+ * module's getProvisionRequest returns a provision request. The vendor
+ * module should process the provisioning request, either by sending it
+ * to a provisioning server, or generating a mock response. The resulting
+ * provisioning response is returned to the VTS test.
+ *
+ * @param provisioningRequest the provisioning request recieved from
+ * the DRM HAL
+ * @param url the default url the HAL implementation provided with the
+ * provisioning request
+ * @return the generated provisioning response
+ */
+ virtual std::vector<uint8_t> handleProvisioningRequest(
+ const std::vector<uint8_t>& provisioningRequest,
+ const std::string& url) = 0;
+
+ /**
+ * Content configuration specifies content-specific parameters associated
+ * with a key request/response transaction. It allows the VTS test to
+ * request keys and use them to perform decryption.
+ */
+ struct ContentConfiguration {
+ /**
+ * Assign a name for this configuration that will be referred to
+ * in log messages.
+ */
+ const std::string name;
+
+ /**
+ * Server to use when requesting a key response. This url will be
+ * passed as a parameter to the vendor vts module along with the
+ * key request to perform the key request transaction.
+ */
+ const std::string serverUrl;
+
+ /**
+ * Initialization data provided to getKeyRequest, e.g. PSSH for CENC
+ * content
+ */
+ const std::vector<uint8_t> initData;
+
+ /**
+ * Mime type provided to getKeyRequest, e.g. "video/mp4", or "cenc"
+ */
+ const std::string mimeType;
+
+ /**
+ * Optional parameters to be associated with the key request
+ */
+ const std::map<std::string, std::string> optionalParameters;
+
+ /**
+ * The keys that will be available once the keys are loaded
+ */
+ struct Key {
+ /**
+ * Indicate if the key content is configured to require secure
+ * buffers,
+ * where the output buffers are protected and cannot be accessed.
+ * A vendor module should provide some content configurations where
+ * isSecure is false, to allow decrypt result verification tests to
+ * be
+ * run.
+ */
+ bool isSecure;
+
+ /**
+ * A key ID identifies a key to use for decryption
+ */
+ const std::vector<uint8_t> keyId;
+
+ /**
+ * The key value is provided to generate expected values for
+ * validating
+ * decryption. If isSecure is false, no key value is required.
+ */
+ const std::vector<uint8_t> keyValue;
+ };
+ std::vector<Key> keys;
+ };
+
+ /**
+ * Return a list of content configurations that can be exercised by the
+ * VTS test.
+ */
+ virtual std::vector<ContentConfiguration> getContentConfigurations() = 0;
+
+ /**
+ * Handle a key request. This function will be called if the HAL
+ * module's getKeyRequest returns a key request. The vendor
+ * module should process the key request, either by sending it
+ * to a license server, or by generating a mock response. The resulting
+ * key response is returned to the VTS test.
+ *
+ * @param keyRequest the key request recieved from the DRM HAL
+ * @param serverUrl the url of the key server that was supplied
+ * by the ContentConfiguration
+ * @return the generated key response
+ */
+ virtual std::vector<uint8_t> handleKeyRequest(
+ const std::vector<uint8_t>& keyRequest,
+ const std::string& serverUrl) = 0;
+};
+
+#endif // DRM_HAL_VENDOR_MODULE_API_H
diff --git a/drm/1.0/vts/functional/drm_hal_vendor_test.cpp b/drm/1.0/vts/functional/drm_hal_vendor_test.cpp
new file mode 100644
index 0000000..dcfee4e
--- /dev/null
+++ b/drm/1.0/vts/functional/drm_hal_vendor_test.cpp
@@ -0,0 +1,980 @@
+/*
+ * Copyright (C) 2017 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_vendor_test@1.0"
+
+#include <android-base/logging.h>
+#include <android/hardware/drm/1.0/ICryptoFactory.h>
+#include <android/hardware/drm/1.0/ICryptoPlugin.h>
+#include <android/hardware/drm/1.0/IDrmFactory.h>
+#include <android/hardware/drm/1.0/IDrmPlugin.h>
+#include <android/hardware/drm/1.0/types.h>
+#include <android/hidl/allocator/1.0/IAllocator.h>
+#include <gtest/gtest.h>
+#include <hidlmemory/mapping.h>
+#include <memory>
+#include <random>
+
+#include "VtsHalHidlTargetTestBase.h"
+#include "drm_hal_vendor_module_api.h"
+#include "vendor_modules.h"
+
+using ::android::hardware::drm::V1_0::BufferType;
+using ::android::hardware::drm::V1_0::DestinationBuffer;
+using ::android::hardware::drm::V1_0::ICryptoFactory;
+using ::android::hardware::drm::V1_0::ICryptoPlugin;
+using ::android::hardware::drm::V1_0::IDrmFactory;
+using ::android::hardware::drm::V1_0::IDrmPlugin;
+using ::android::hardware::drm::V1_0::KeyedVector;
+using ::android::hardware::drm::V1_0::KeyValue;
+using ::android::hardware::drm::V1_0::KeyRequestType;
+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::SecureStop;
+using ::android::hardware::drm::V1_0::SecureStopId;
+using ::android::hardware::drm::V1_0::SessionId;
+using ::android::hardware::drm::V1_0::SharedBuffer;
+using ::android::hardware::drm::V1_0::Status;
+using ::android::hardware::drm::V1_0::SubSample;
+
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hidl::allocator::V1_0::IAllocator;
+using ::android::hidl::memory::V1_0::IMemory;
+using ::android::sp;
+
+using std::string;
+using std::unique_ptr;
+using std::random_device;
+using std::map;
+using std::mt19937;
+using std::vector;
+
+#define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk())
+#define EXPECT_OK(ret) EXPECT_TRUE(ret.isOk())
+
+static const uint8_t kInvalidUUID[16] = {
+ 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
+ 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
+};
+
+static drm_vts::VendorModules* gVendorModules = nullptr;
+
+class DrmHalVendorFactoryTest : public testing::TestWithParam<std::string> {
+ public:
+ DrmHalVendorFactoryTest()
+ : vendorModule(gVendorModules ? static_cast<DrmHalVTSVendorModule_V1*>(
+ gVendorModules->getVendorModule(
+ GetParam()))
+ : nullptr) {}
+
+ virtual ~DrmHalVendorFactoryTest() {}
+
+ virtual void 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());
+
+ ASSERT_NE(vendorModule, nullptr);
+ string name = vendorModule->getServiceName();
+ drmFactory =
+ ::testing::VtsHalHidlTargetTestBase::getService<IDrmFactory>(
+ name != "default" ? name : "drm");
+ ASSERT_NE(drmFactory, nullptr);
+ cryptoFactory =
+ ::testing::VtsHalHidlTargetTestBase::getService<ICryptoFactory>(
+ name != "default" ? name : "crypto");
+ ASSERT_NE(cryptoFactory, nullptr);
+ }
+
+ virtual void TearDown() override {}
+
+ protected:
+ hidl_array<uint8_t, 16> getVendorUUID() {
+ vector<uint8_t> uuid = vendorModule->getUUID();
+ return hidl_array<uint8_t, 16>(&uuid[0]);
+ }
+
+ sp<IDrmFactory> drmFactory;
+ sp<ICryptoFactory> cryptoFactory;
+ unique_ptr<DrmHalVTSVendorModule_V1> vendorModule;
+};
+
+/**
+ * Ensure the factory supports its scheme UUID
+ */
+TEST_P(DrmHalVendorFactoryTest, VendorPluginSupported) {
+ EXPECT_TRUE(drmFactory->isCryptoSchemeSupported(getVendorUUID()));
+ EXPECT_TRUE(cryptoFactory->isCryptoSchemeSupported(getVendorUUID()));
+}
+
+/**
+ * Ensure the factory doesn't support an invalid scheme UUID
+ */
+TEST_P(DrmHalVendorFactoryTest, InvalidPluginNotSupported) {
+ EXPECT_FALSE(drmFactory->isCryptoSchemeSupported(kInvalidUUID));
+ EXPECT_FALSE(cryptoFactory->isCryptoSchemeSupported(kInvalidUUID));
+}
+
+/**
+ * Ensure vendor drm plugin can be created
+ */
+TEST_P(DrmHalVendorFactoryTest, CreateVendorDrmPlugin) {
+ hidl_string packageName("android.hardware.drm.test");
+ auto res = drmFactory->createPlugin(
+ getVendorUUID(), packageName,
+ [&](Status status, const sp<IDrmPlugin>& plugin) {
+ EXPECT_EQ(Status::OK, status);
+ EXPECT_NE(plugin, nullptr);
+ });
+ EXPECT_OK(res);
+}
+
+/**
+ * Ensure vendor crypto plugin can be created
+ */
+TEST_P(DrmHalVendorFactoryTest, CreateVendorCryptoPlugin) {
+ hidl_vec<uint8_t> initVec;
+ auto res = cryptoFactory->createPlugin(
+ getVendorUUID(), initVec,
+ [&](Status status, const sp<ICryptoPlugin>& plugin) {
+ EXPECT_EQ(Status::OK, status);
+ EXPECT_NE(plugin, nullptr);
+ });
+ EXPECT_OK(res);
+}
+
+/**
+ * Ensure invalid drm plugin can't be created
+ */
+TEST_P(DrmHalVendorFactoryTest, CreateInvalidDrmPlugin) {
+ hidl_string packageName("android.hardware.drm.test");
+ auto res = drmFactory->createPlugin(
+ kInvalidUUID, packageName,
+ [&](Status status, const sp<IDrmPlugin>& plugin) {
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+ EXPECT_EQ(plugin, nullptr);
+ });
+ EXPECT_OK(res);
+}
+
+/**
+ * Ensure invalid crypto plugin can't be created
+ */
+TEST_P(DrmHalVendorFactoryTest, CreateInvalidCryptoPlugin) {
+ hidl_vec<uint8_t> initVec;
+ auto res = cryptoFactory->createPlugin(
+ kInvalidUUID, initVec,
+ [&](Status status, const sp<ICryptoPlugin>& plugin) {
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+ EXPECT_EQ(plugin, nullptr);
+ });
+ EXPECT_OK(res);
+}
+
+class DrmHalVendorPluginTest : public DrmHalVendorFactoryTest {
+ public:
+ virtual ~DrmHalVendorPluginTest() {}
+ virtual void SetUp() override {
+ // Create factories
+ DrmHalVendorFactoryTest::SetUp();
+
+ hidl_string packageName("android.hardware.drm.test");
+ auto res = drmFactory->createPlugin(
+ getVendorUUID(), packageName,
+ [this](Status status, const sp<IDrmPlugin>& plugin) {
+ EXPECT_EQ(Status::OK, status);
+ ASSERT_NE(plugin, nullptr);
+ drmPlugin = plugin;
+ });
+ ASSERT_OK(res);
+
+ hidl_vec<uint8_t> initVec;
+ res = cryptoFactory->createPlugin(
+ getVendorUUID(), initVec,
+ [this](Status status, const sp<ICryptoPlugin>& plugin) {
+ EXPECT_EQ(Status::OK, status);
+ ASSERT_NE(plugin, nullptr);
+ cryptoPlugin = plugin;
+ });
+ ASSERT_OK(res);
+ }
+
+ virtual void TearDown() override {}
+
+ SessionId openSession();
+ void closeSession(const SessionId& sessionId);
+ sp<IMemory> getDecryptMemory(size_t size, size_t index);
+
+ protected:
+ sp<IDrmPlugin> drmPlugin;
+ sp<ICryptoPlugin> cryptoPlugin;
+};
+
+/**
+ * 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(DrmHalVendorPluginTest, DoProvisioning) {
+ hidl_string certificateType;
+ hidl_string certificateAuthority;
+ hidl_vec<uint8_t> provisionRequest;
+ hidl_string defaultUrl;
+ auto res = drmPlugin->getProvisionRequest(
+ certificateType, certificateAuthority,
+ [&](Status status, const hidl_vec<uint8_t>& request,
+ const hidl_string& url) {
+ if (status == Status::OK) {
+ EXPECT_NE(request.size(), 0u);
+ provisionRequest = request;
+ defaultUrl = url;
+ } else if (status == Status::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);
+ }
+}
+
+/**
+ * The DRM HAL should return BAD_VALUE if an empty provisioning
+ * response is provided.
+ */
+TEST_P(DrmHalVendorPluginTest, ProvideEmptyProvisionResponse) {
+ hidl_vec<uint8_t> response;
+ auto res = drmPlugin->provideProvisionResponse(
+ response, [&](Status status, const hidl_vec<uint8_t>&,
+ const hidl_vec<uint8_t>&) {
+ EXPECT_EQ(Status::BAD_VALUE, status);
+ });
+ EXPECT_OK(res);
+}
+
+/**
+ * Helper method to open a session and verify that a non-empty
+ * session ID is returned
+ */
+SessionId DrmHalVendorPluginTest::openSession() {
+ SessionId sessionId;
+
+ auto res = drmPlugin->openSession([&](Status status, const SessionId& id) {
+ EXPECT_EQ(Status::OK, status);
+ EXPECT_NE(id.size(), 0u);
+ sessionId = id;
+ });
+ EXPECT_OK(res);
+ return sessionId;
+}
+
+/**
+ * Helper method to close a session
+ */
+void DrmHalVendorPluginTest::closeSession(const SessionId& sessionId) {
+ Status status = drmPlugin->closeSession(sessionId);
+ EXPECT_EQ(Status::OK, status);
+}
+
+/**
+ * Test that a session can be opened and closed
+ */
+TEST_P(DrmHalVendorPluginTest, OpenCloseSession) {
+ auto sessionId = openSession();
+ closeSession(sessionId);
+}
+
+/**
+ * Test that attempting to close an invalid (empty) sessionId
+ * is prohibited with the documented error code.
+ */
+TEST_P(DrmHalVendorPluginTest, CloseInvalidSession) {
+ SessionId invalidSessionId;
+ Status status = drmPlugin->closeSession(invalidSessionId);
+ EXPECT_EQ(Status::BAD_VALUE, status);
+}
+
+/**
+ * Test that attempting to close a valid session twice
+ * is prohibited with the documented error code.
+ */
+TEST_P(DrmHalVendorPluginTest, CloseClosedSession) {
+ auto sessionId = openSession();
+ closeSession(sessionId);
+ Status status = drmPlugin->closeSession(sessionId);
+ EXPECT_EQ(Status::ERROR_DRM_SESSION_NOT_OPENED, status);
+}
+
+/**
+ * A get key request should fail if no sessionId is provided
+ */
+TEST_P(DrmHalVendorPluginTest, GetKeyRequestNoSession) {
+ SessionId invalidSessionId;
+ hidl_vec<uint8_t> initData;
+ hidl_string mimeType = "video/mp4";
+ KeyedVector optionalParameters;
+ auto res = drmPlugin->getKeyRequest(
+ invalidSessionId, initData, mimeType, KeyType::STREAMING,
+ optionalParameters,
+ [&](Status status, const hidl_vec<uint8_t>&, KeyRequestType,
+ const hidl_string&) { EXPECT_EQ(Status::BAD_VALUE, status); });
+ EXPECT_OK(res);
+}
+
+/**
+ * Test that an empty sessionID returns BAD_VALUE
+ */
+TEST_P(DrmHalVendorPluginTest, ProvideKeyResponseEmptySessionId) {
+ SessionId session;
+
+ hidl_vec<uint8_t> keyResponse = {0x7b, 0x22, 0x6b, 0x65,
+ 0x79, 0x73, 0x22, 0x3a};
+ auto res = drmPlugin->provideKeyResponse(
+ session, keyResponse,
+ [&](Status status, const hidl_vec<uint8_t>& keySetId) {
+ EXPECT_EQ(Status::BAD_VALUE, status);
+ EXPECT_EQ(keySetId.size(), 0u);
+ });
+ EXPECT_OK(res);
+}
+
+/**
+ * Test that an empty key response returns BAD_VALUE
+ */
+TEST_P(DrmHalVendorPluginTest, ProvideKeyResponseEmptyResponse) {
+ SessionId session = openSession();
+ hidl_vec<uint8_t> emptyResponse;
+ auto res = drmPlugin->provideKeyResponse(
+ session, emptyResponse,
+ [&](Status status, const hidl_vec<uint8_t>& keySetId) {
+ EXPECT_EQ(Status::BAD_VALUE, status);
+ EXPECT_EQ(keySetId.size(), 0u);
+ });
+ EXPECT_OK(res);
+ closeSession(session);
+}
+
+/**
+ * Test that the plugin either doesn't support getting
+ * secure stops, or has no secure stops available after
+ * clearing them.
+ */
+TEST_P(DrmHalVendorPluginTest, GetSecureStops) {
+ // There may be secure stops, depending on if there were keys
+ // loaded and unloaded previously. Clear them to get to a known
+ // state, then make sure there are none.
+ auto res = drmPlugin->getSecureStops(
+ [&](Status status, const hidl_vec<SecureStop>&) {
+ if (status != Status::OK) {
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+ }
+ });
+ EXPECT_OK(res);
+
+ res = drmPlugin->getSecureStops(
+ [&](Status status, const hidl_vec<SecureStop>& secureStops) {
+ if (status == Status::OK) {
+ EXPECT_EQ(secureStops.size(), 0u);
+ } else {
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+ }
+ });
+ EXPECT_OK(res);
+}
+
+/**
+ * Test that the clearkey plugin returns BAD_VALUE if
+ * an empty ssid is provided.
+ */
+TEST_P(DrmHalVendorPluginTest, GetSecureStopEmptySSID) {
+ SecureStopId ssid;
+ auto res = drmPlugin->getSecureStop(
+ ssid, [&](Status status, const SecureStop&) {
+ EXPECT_EQ(Status::BAD_VALUE, status);
+ });
+ EXPECT_OK(res);
+}
+
+/**
+ * Test that releasing all secure stops either isn't supported
+ * or is completed successfully
+ */
+TEST_P(DrmHalVendorPluginTest, ReleaseAllSecureStops) {
+ Status status = drmPlugin->releaseAllSecureStops();
+ EXPECT_TRUE(status == Status::OK ||
+ status == Status::ERROR_DRM_CANNOT_HANDLE);
+}
+
+/**
+ * Releasing a secure stop without first getting one and sending it to the
+ * server to get a valid SSID should return ERROR_DRM_INVALID_STATE.
+ * This is an optional API so it can also return CANNOT_HANDLE.
+ */
+TEST_P(DrmHalVendorPluginTest, ReleaseSecureStopSequenceError) {
+ SecureStopId ssid = {1, 2, 3, 4};
+ Status status = drmPlugin->releaseSecureStop(ssid);
+ EXPECT_TRUE(status == Status::ERROR_DRM_INVALID_STATE ||
+ status == Status::ERROR_DRM_CANNOT_HANDLE);
+}
+
+/**
+ * Test that releasing a specific secure stop with an empty ssid
+ * return BAD_VALUE. This is an optional API so it can also return
+ * CANNOT_HANDLE.
+ */
+TEST_P(DrmHalVendorPluginTest, ReleaseSecureStopEmptySSID) {
+ SecureStopId ssid;
+ Status status = drmPlugin->releaseSecureStop(ssid);
+ EXPECT_TRUE(status == Status::BAD_VALUE ||
+ status == Status::ERROR_DRM_CANNOT_HANDLE);
+}
+
+/**
+ * The following five tests verify that the properties
+ * defined in the MediaDrm API are supported by
+ * the plugin.
+ */
+TEST_P(DrmHalVendorPluginTest, GetVendorProperty) {
+ auto res = drmPlugin->getPropertyString(
+ "vendor", [&](Status status, const hidl_string& value) {
+ EXPECT_EQ(Status::OK, status);
+ EXPECT_NE(value.size(), 0u);
+ });
+ EXPECT_OK(res);
+}
+
+TEST_P(DrmHalVendorPluginTest, GetVersionProperty) {
+ auto res = drmPlugin->getPropertyString(
+ "version", [&](Status status, const hidl_string& value) {
+ EXPECT_EQ(Status::OK, status);
+ EXPECT_NE(value.size(), 0u);
+ });
+ EXPECT_OK(res);
+}
+
+TEST_P(DrmHalVendorPluginTest, GetDescriptionProperty) {
+ auto res = drmPlugin->getPropertyString(
+ "description", [&](Status status, const hidl_string& value) {
+ EXPECT_EQ(Status::OK, status);
+ EXPECT_NE(value.size(), 0u);
+ });
+ EXPECT_OK(res);
+}
+
+TEST_P(DrmHalVendorPluginTest, GetAlgorithmsProperty) {
+ auto res = drmPlugin->getPropertyString(
+ "algorithms", [&](Status status, const hidl_string& value) {
+ if (status == Status::OK) {
+ EXPECT_NE(value.size(), 0u);
+ } else {
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+ }
+ });
+ EXPECT_OK(res);
+}
+
+TEST_P(DrmHalVendorPluginTest, GetPropertyUniqueDeviceID) {
+ auto res = drmPlugin->getPropertyByteArray(
+ "deviceUniqueId",
+ [&](Status status, const hidl_vec<uint8_t>& value) {
+ if (status == Status::OK) {
+ EXPECT_NE(value.size(), 0u);
+ } else {
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+ }
+ });
+ EXPECT_OK(res);
+}
+
+/**
+ * Test that attempting to read invalid string and byte array
+ * properties returns the documented error code.
+ */
+TEST_P(DrmHalVendorPluginTest, GetInvalidStringProperty) {
+ auto res = drmPlugin->getPropertyString(
+ "invalid", [&](Status status, const hidl_string&) {
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+ });
+ EXPECT_OK(res);
+}
+
+TEST_P(DrmHalVendorPluginTest, GetInvalidByteArrayProperty) {
+ auto res = drmPlugin->getPropertyByteArray(
+ "invalid", [&](Status status, const hidl_vec<uint8_t>&) {
+ EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status);
+ });
+ EXPECT_OK(res);
+}
+
+/**
+ * Test that setting invalid string and byte array properties returns
+ * the expected status value.
+ */
+TEST_P(DrmHalVendorPluginTest, SetStringPropertyNotSupported) {
+ EXPECT_EQ(drmPlugin->setPropertyString("awefijaeflijwef", "value"),
+ Status::ERROR_DRM_CANNOT_HANDLE);
+}
+
+TEST_P(DrmHalVendorPluginTest, SetByteArrayPropertyNotSupported) {
+ hidl_vec<uint8_t> value;
+ EXPECT_EQ(drmPlugin->setPropertyByteArray("awefijaeflijwef", value),
+ Status::ERROR_DRM_CANNOT_HANDLE);
+}
+
+/**
+ * Test that setting an invalid cipher algorithm returns
+ * the expected status value.
+ */
+TEST_P(DrmHalVendorPluginTest, SetCipherInvalidAlgorithm) {
+ SessionId session = openSession();
+ hidl_string algorithm;
+ Status status = drmPlugin->setCipherAlgorithm(session, algorithm);
+ EXPECT_EQ(Status::BAD_VALUE, status);
+ closeSession(session);
+}
+
+/**
+ * Test that setting a cipher algorithm with no session returns
+ * the expected status value.
+ */
+TEST_P(DrmHalVendorPluginTest, SetCipherAlgorithmNoSession) {
+ SessionId session;
+ hidl_string algorithm = "AES/CBC/NoPadding";
+ Status status = drmPlugin->setCipherAlgorithm(session, algorithm);
+ EXPECT_EQ(Status::BAD_VALUE, status);
+}
+
+/**
+ * Test that setting a valid cipher algorithm returns
+ * the expected status value. It is not required that all
+ * vendor modules support this algorithm, but they must
+ * either accept it or return ERROR_DRM_CANNOT_HANDLE
+ */
+TEST_P(DrmHalVendorPluginTest, SetCipherAlgorithm) {
+ SessionId session = openSession();
+ ;
+ hidl_string algorithm = "AES/CBC/NoPadding";
+ Status status = drmPlugin->setCipherAlgorithm(session, algorithm);
+ EXPECT_TRUE(status == Status::OK ||
+ status == Status::ERROR_DRM_CANNOT_HANDLE);
+ closeSession(session);
+}
+
+/**
+ * Test that setting an invalid mac algorithm returns
+ * the expected status value.
+ */
+TEST_P(DrmHalVendorPluginTest, SetMacInvalidAlgorithm) {
+ SessionId session = openSession();
+ hidl_string algorithm;
+ Status status = drmPlugin->setMacAlgorithm(session, algorithm);
+ EXPECT_EQ(Status::BAD_VALUE, status);
+ closeSession(session);
+}
+
+/**
+ * Test that setting a mac algorithm with no session returns
+ * the expected status value.
+ */
+TEST_P(DrmHalVendorPluginTest, SetMacNullAlgorithmNoSession) {
+ SessionId session;
+ hidl_string algorithm = "HmacSHA256";
+ Status status = drmPlugin->setMacAlgorithm(session, algorithm);
+ EXPECT_EQ(Status::BAD_VALUE, status);
+}
+
+/**
+ * Test that setting a valid mac algorithm returns
+ * the expected status value. It is not required that all
+ * vendor modules support this algorithm, but they must
+ * either accept it or return ERROR_DRM_CANNOT_HANDLE
+ */
+TEST_P(DrmHalVendorPluginTest, SetMacAlgorithm) {
+ SessionId session = openSession();
+ hidl_string algorithm = "HmacSHA256";
+ Status status = drmPlugin->setMacAlgorithm(session, algorithm);
+ EXPECT_TRUE(status == Status::OK ||
+ status == Status::ERROR_DRM_CANNOT_HANDLE);
+ closeSession(session);
+}
+
+/**
+ * The Generic* methods provide general purpose crypto operations
+ * that may be used for applications other than DRM. They leverage
+ * the hardware root of trust and secure key distribution mechanisms
+ * of a DRM system to enable app-specific crypto functionality where
+ * the crypto keys are not exposed outside of the trusted execution
+ * environment.
+ *
+ * Generic encrypt/decrypt/sign/verify should fail on invalid
+ * inputs, e.g. empty sessionId
+ */
+TEST_P(DrmHalVendorPluginTest, GenericEncryptNoSession) {
+ SessionId session;
+ hidl_vec<uint8_t> keyId, input, iv;
+ auto res = drmPlugin->encrypt(
+ session, keyId, input, iv,
+ [&](Status status, const hidl_vec<uint8_t>&) {
+ EXPECT_EQ(Status::ERROR_DRM_SESSION_NOT_OPENED, status);
+ });
+ EXPECT_OK(res);
+}
+
+TEST_P(DrmHalVendorPluginTest, GenericDecryptNoSession) {
+ SessionId session;
+ hidl_vec<uint8_t> keyId, input, iv;
+ auto res = drmPlugin->decrypt(
+ session, keyId, input, iv,
+ [&](Status status, const hidl_vec<uint8_t>&) {
+ EXPECT_EQ(Status::ERROR_DRM_SESSION_NOT_OPENED, status);
+ });
+ EXPECT_OK(res);
+}
+
+TEST_P(DrmHalVendorPluginTest, GenericSignNoSession) {
+ SessionId session;
+ hidl_vec<uint8_t> keyId, message;
+ auto res = drmPlugin->sign(
+ session, keyId, message,
+ [&](Status status, const hidl_vec<uint8_t>&) {
+ EXPECT_EQ(Status::ERROR_DRM_SESSION_NOT_OPENED, status);
+ });
+ EXPECT_OK(res);
+}
+
+TEST_P(DrmHalVendorPluginTest, GenericVerifyNoSession) {
+ SessionId session;
+ hidl_vec<uint8_t> keyId, message, signature;
+ auto res = drmPlugin->verify(
+ session, keyId, message, signature, [&](Status status, bool) {
+ EXPECT_EQ(Status::ERROR_DRM_SESSION_NOT_OPENED, status);
+ });
+ EXPECT_OK(res);
+}
+
+TEST_P(DrmHalVendorPluginTest, GenericSignRSANoSession) {
+ SessionId session;
+ hidl_string algorithm;
+ hidl_vec<uint8_t> message, wrappedKey;
+ auto res = drmPlugin->signRSA(session, algorithm, message, wrappedKey,
+ [&](Status status, const hidl_vec<uint8_t>&) {
+ EXPECT_EQ(Status::BAD_VALUE, status);
+ });
+ EXPECT_OK(res);
+}
+
+/**
+ * Exercise the requiresSecureDecoderComponent method. Additional tests
+ * will verify positive cases with specific vendor content configurations.
+ * Below we just test the negative cases.
+ */
+
+/**
+ * Verify that requiresSecureDecoderComponent handles empty mimetype.
+ */
+TEST_P(DrmHalVendorPluginTest, RequiresSecureDecoderEmptyMimeType) {
+ EXPECT_FALSE(cryptoPlugin->requiresSecureDecoderComponent(""));
+}
+
+/**
+ * Verify that requiresSecureDecoderComponent handles invalid mimetype.
+ */
+TEST_P(DrmHalVendorPluginTest, RequiresSecureDecoderInvalidMimeType) {
+ EXPECT_FALSE(cryptoPlugin->requiresSecureDecoderComponent("bad"));
+}
+
+/**
+ * CryptoPlugin tests
+ */
+
+/**
+ * Exercise the NotifyResolution API. There is no observable result,
+ * just call the method for coverage.
+ */
+TEST_P(DrmHalVendorPluginTest, NotifyResolution) {
+ cryptoPlugin->notifyResolution(1920, 1080);
+}
+
+/**
+ * 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> DrmHalVendorPluginTest::getDecryptMemory(size_t size,
+ size_t index) {
+ sp<IAllocator> ashmemAllocator = IAllocator::getService("ashmem");
+ EXPECT_NE(ashmemAllocator, nullptr);
+
+ 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(mappedMemory, nullptr);
+ res = cryptoPlugin->setSharedBufferBase(hidlMemory, index);
+ EXPECT_OK(res);
+ return mappedMemory;
+}
+
+/**
+ * Exercise the setMediaDrmSession method. setMediaDrmSession
+ * is used to associate a drm session with a crypto session.
+ */
+TEST_P(DrmHalVendorPluginTest, SetMediaDrmSession) {
+ auto sessionId = openSession();
+ Status status = cryptoPlugin->setMediaDrmSession(sessionId);
+ EXPECT_EQ(Status::OK, status);
+ closeSession(sessionId);
+}
+
+/**
+ * setMediaDrmSession with a closed session id
+ */
+TEST_P(DrmHalVendorPluginTest, SetMediaDrmSessionClosedSession) {
+ auto sessionId = openSession();
+ closeSession(sessionId);
+ Status status = cryptoPlugin->setMediaDrmSession(sessionId);
+ EXPECT_EQ(Status::ERROR_DRM_SESSION_NOT_OPENED, status);
+}
+
+/**
+ * Decrypt tests
+ */
+
+class DrmHalVendorDecryptTest : public DrmHalVendorPluginTest {
+ public:
+ DrmHalVendorDecryptTest() = default;
+ virtual ~DrmHalVendorDecryptTest() {}
+
+ protected:
+ void loadKeys(const SessionId& sessionId,
+ const DrmHalVTSVendorModule_V1::ContentConfiguration&
+ configuration);
+ void fillRandom(const sp<IMemory>& memory);
+ KeyedVector toHidlKeyedVector(const map<string, string>& params);
+ hidl_array<uint8_t, 16> toHidlArray(const vector<uint8_t>& vec) {
+ EXPECT_EQ(vec.size(), 16u);
+ return hidl_array<uint8_t, 16>(&vec[0]);
+ }
+};
+
+KeyedVector DrmHalVendorDecryptTest::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);
+}
+
+/**
+ * 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.
+ */
+void DrmHalVendorDecryptTest::loadKeys(
+ const SessionId& sessionId,
+ const DrmHalVTSVendorModule_V1::ContentConfiguration& configuration) {
+ hidl_vec<uint8_t> keyRequest;
+ auto res = drmPlugin->getKeyRequest(
+ sessionId, configuration.initData, configuration.mimeType,
+ KeyType::STREAMING,
+ toHidlKeyedVector(configuration.optionalParameters),
+ [&](Status status, const hidl_vec<uint8_t>& request,
+ KeyRequestType type, const hidl_string&) {
+ EXPECT_EQ(Status::OK, status)
+ << "Failed to get "
+ "key request for configuration "
+ << configuration.name;
+ EXPECT_EQ(type, KeyRequestType::INITIAL);
+ EXPECT_NE(request.size(), 0u) << "Expected key request size"
+ " to have length > 0 bytes";
+ keyRequest = request;
+ });
+ EXPECT_OK(res);
+
+ /**
+ * 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";
+
+ res = drmPlugin->provideKeyResponse(
+ sessionId, keyResponse,
+ [&](Status status, const hidl_vec<uint8_t>&) {
+ EXPECT_EQ(Status::OK, status)
+ << "Failure providing "
+ "key response for configuration "
+ << configuration.name;
+ });
+ EXPECT_OK(res);
+}
+
+void DrmHalVendorDecryptTest::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();
+ }
+}
+
+TEST_P(DrmHalVendorDecryptTest, ValidateConfigurations) {
+ vector<DrmHalVTSVendorModule_V1::ContentConfiguration> configurations =
+ vendorModule->getContentConfigurations();
+ const char* kVendorStr = "Vendor module ";
+ for (auto config : configurations) {
+ ASSERT_TRUE(config.name.size() > 0) << kVendorStr << "has no name";
+ ASSERT_TRUE(config.serverUrl.size() > 0) << kVendorStr
+ << "has no serverUrl";
+ ASSERT_TRUE(config.initData.size() > 0) << kVendorStr
+ << "has no init data";
+ 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) {
+ ASSERT_TRUE(key.keyId.size() > 0) << kVendorStr
+ << " has zero length keyId";
+ ASSERT_TRUE(key.keyId.size() > 0) << kVendorStr
+ << " has zero length key value";
+ }
+ }
+}
+
+/**
+ * Positive decrypt test. "Decrypt" a single clear
+ * segment. Verify data matches.
+ */
+TEST_P(DrmHalVendorDecryptTest, ClearSegmentTest) {
+ vector<DrmHalVTSVendorModule_V1::ContentConfiguration> configurations =
+ vendorModule->getContentConfigurations();
+ for (auto config : configurations) {
+ const size_t kSegmentSize = 1024;
+ const size_t kSegmentIndex = 0;
+ uint8_t iv[16] = {0};
+
+ sp<IMemory> sharedMemory =
+ getDecryptMemory(kSegmentSize * 2, kSegmentIndex);
+
+ SharedBuffer sourceBuffer = {
+ .bufferId = kSegmentIndex, .offset = 0, .size = kSegmentSize};
+ fillRandom(sharedMemory);
+
+ DestinationBuffer destBuffer = {.type = BufferType::SHARED_MEMORY,
+ {.bufferId = kSegmentIndex,
+ .offset = kSegmentSize,
+ .size = kSegmentSize},
+ .secureMemory = nullptr};
+
+ Pattern noPattern = {0, 0};
+ vector<SubSample> subSamples = {{.numBytesOfClearData = kSegmentSize,
+ .numBytesOfEncryptedData = 0}};
+ uint64_t offset = 0;
+
+ auto sessionId = openSession();
+ loadKeys(sessionId, config);
+
+ Status status = cryptoPlugin->setMediaDrmSession(sessionId);
+ EXPECT_EQ(Status::OK, status);
+
+ const bool kNotSecure = false;
+ auto res = cryptoPlugin->decrypt(
+ kNotSecure, toHidlArray(config.keys[0].keyId), iv,
+ Mode::UNENCRYPTED, noPattern, subSamples, sourceBuffer, offset,
+ destBuffer, [&](Status status, uint32_t bytesWritten,
+ string detailedError) {
+ EXPECT_EQ(Status::OK, status) << "Failure in decryption "
+ "for configuration "
+ << config.name << ": "
+ << detailedError;
+ EXPECT_EQ(bytesWritten, kSegmentSize);
+ });
+ EXPECT_OK(res);
+ uint8_t* base = static_cast<uint8_t*>(
+ static_cast<void*>(sharedMemory->getPointer()));
+
+ EXPECT_EQ(0,
+ memcmp(static_cast<void*>(base),
+ static_cast<void*>(base + kSegmentSize), kSegmentSize))
+ << "decrypt data mismatch";
+ closeSession(sessionId);
+ }
+}
+
+/**
+ * Instantiate the set of test cases for each vendor module
+ */
+
+INSTANTIATE_TEST_CASE_P(
+ DrmHalVendorFactoryTestCases, DrmHalVendorFactoryTest,
+ testing::ValuesIn(gVendorModules->getVendorModulePaths()));
+
+INSTANTIATE_TEST_CASE_P(
+ DrmHalVendorPluginTestCases, DrmHalVendorPluginTest,
+ testing::ValuesIn(gVendorModules->getVendorModulePaths()));
+
+INSTANTIATE_TEST_CASE_P(
+ DrmHalVendorDecryptTestCases, DrmHalVendorDecryptTest,
+ testing::ValuesIn(gVendorModules->getVendorModulePaths()));
+
+int main(int argc, char** argv) {
+ gVendorModules =
+ new drm_vts::VendorModules("/data/nativetest/drm_hidl_test/vendor");
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/drm/1.0/vts/functional/shared_library.cpp b/drm/1.0/vts/functional/shared_library.cpp
new file mode 100644
index 0000000..6658150
--- /dev/null
+++ b/drm/1.0/vts/functional/shared_library.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 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-shared-library"
+
+#include <dlfcn.h>
+#include <shared_library.h>
+
+using std::string;
+
+namespace drm_vts {
+
+SharedLibrary::SharedLibrary(const string& path) {
+ mLibHandle = dlopen(path.c_str(), RTLD_NOW);
+}
+
+SharedLibrary::~SharedLibrary() {
+ if (mLibHandle != NULL) {
+ dlclose(mLibHandle);
+ mLibHandle = NULL;
+ }
+}
+
+bool SharedLibrary::operator!() const {
+ return mLibHandle == NULL;
+}
+
+void* SharedLibrary::lookup(const char* symbol) const {
+ if (!mLibHandle) {
+ return NULL;
+ }
+
+ // Clear last error before we load the symbol again,
+ // in case the caller didn't retrieve it.
+ (void)dlerror();
+ return dlsym(mLibHandle, symbol);
+}
+
+const char* SharedLibrary::lastError() const {
+ const char* error = dlerror();
+ return error ? error : "No errors or unknown error";
+}
+};
diff --git a/drm/1.0/vts/functional/shared_library.h b/drm/1.0/vts/functional/shared_library.h
new file mode 100644
index 0000000..1f32243
--- /dev/null
+++ b/drm/1.0/vts/functional/shared_library.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 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 SHARED_LIBRARY_H_
+#define SHARED_LIBRARY_H_
+
+#include <string>
+#include <vector>
+
+namespace drm_vts {
+class SharedLibrary {
+ public:
+ explicit SharedLibrary(const std::string& path);
+ ~SharedLibrary();
+
+ bool operator!() const;
+ void* lookup(const char* symbol) const;
+ const char* lastError() const;
+
+ private:
+ void* mLibHandle;
+
+ SharedLibrary(const SharedLibrary&) = delete;
+ void operator=(const SharedLibrary&) = delete;
+};
+};
+
+#endif // SHARED_LIBRARY_H_
diff --git a/drm/1.0/vts/functional/vendor/lib/libvtswidevine.so b/drm/1.0/vts/functional/vendor/lib/libvtswidevine.so
new file mode 100755
index 0000000..d365b34
--- /dev/null
+++ b/drm/1.0/vts/functional/vendor/lib/libvtswidevine.so
Binary files differ
diff --git a/drm/1.0/vts/functional/vendor_modules.cpp b/drm/1.0/vts/functional/vendor_modules.cpp
new file mode 100644
index 0000000..34af6f8
--- /dev/null
+++ b/drm/1.0/vts/functional/vendor_modules.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2017 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 <utils/Log.h>
+#include <memory>
+
+#include "shared_library.h"
+#include "vendor_modules.h"
+
+using std::string;
+using std::vector;
+using std::unique_ptr;
+
+namespace drm_vts {
+vector<string> VendorModules::getVendorModulePaths() {
+ if (mModuleList.size() > 0) {
+ return mModuleList;
+ }
+
+ DIR* dir = opendir(mModulesPath.c_str());
+ if (dir == NULL) {
+ ALOGE("Unable to open drm VTS vendor directory %s",
+ mModulesPath.c_str());
+ return mModuleList;
+ }
+
+ struct dirent* entry;
+ while ((entry = readdir(dir))) {
+ string fullpath = mModulesPath + "/" + entry->d_name;
+ if (endsWith(fullpath, ".so")) {
+ mModuleList.push_back(fullpath);
+ }
+ }
+
+ closedir(dir);
+ return mModuleList;
+}
+
+DrmHalVTSVendorModule* VendorModules::getVendorModule(const string& path) {
+ unique_ptr<SharedLibrary>& library = mOpenLibraries[path];
+ if (!library) {
+ library = unique_ptr<SharedLibrary>(new SharedLibrary(path));
+ if (!library) {
+ ALOGE("failed to map shared library %s", path.c_str());
+ return NULL;
+ }
+ }
+ 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.0/vts/functional/vendor_modules.h b/drm/1.0/vts/functional/vendor_modules.h
new file mode 100644
index 0000000..5371a0d
--- /dev/null
+++ b/drm/1.0/vts/functional/vendor_modules.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 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 "shared_library.h"
+
+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& path) : mModulesPath(path) {}
+ ~VendorModules() {}
+
+ /**
+ * Return a list of paths to available vendor modules.
+ */
+ std::vector<std::string> getVendorModulePaths();
+
+ /**
+ * Retrieve a DrmHalVTSVendorModule given its full path. The
+ * getAPIVersion method can be used to determine the versioned
+ * subclass type.
+ */
+ DrmHalVTSVendorModule* getVendorModule(const std::string& path);
+
+ private:
+ std::string mModulesPath;
+ std::vector<std::string> mModuleList;
+ std::map<std::string, std::unique_ptr<SharedLibrary>> mOpenLibraries;
+
+ inline bool endsWith(const std::string& str, const std::string& suffix) {
+ 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
diff --git a/drm/Android.bp b/drm/Android.bp
index bbb3e4b..33f70eb 100644
--- a/drm/Android.bp
+++ b/drm/Android.bp
@@ -1,4 +1,5 @@
// This is an autogenerated file, do not edit.
subdirs = [
"1.0",
+ "1.0/vts/functional",
]