mirror of
https://github.com/Evolution-X/hardware_interfaces
synced 2026-02-01 11:36:00 +00:00
Merge "Add VTS tests for drm+crypto HALs" into oc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
ebf7f3213b
BIN
drm/1.0/vts/doc/Drm_Vendor_Modules_v1.pdf
Normal file
BIN
drm/1.0/vts/doc/Drm_Vendor_Modules_v1.pdf
Normal file
Binary file not shown.
46
drm/1.0/vts/functional/Android.bp
Normal file
46
drm/1.0/vts/functional/Android.bp
Normal file
@@ -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",
|
||||
],
|
||||
}
|
||||
904
drm/1.0/vts/functional/drm_hal_clearkey_test.cpp
Normal file
904
drm/1.0/vts/functional/drm_hal_clearkey_test.cpp
Normal file
@@ -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);
|
||||
}
|
||||
213
drm/1.0/vts/functional/drm_hal_vendor_module_api.h
Normal file
213
drm/1.0/vts/functional/drm_hal_vendor_module_api.h
Normal file
@@ -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
|
||||
980
drm/1.0/vts/functional/drm_hal_vendor_test.cpp
Normal file
980
drm/1.0/vts/functional/drm_hal_vendor_test.cpp
Normal file
@@ -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();
|
||||
}
|
||||
56
drm/1.0/vts/functional/shared_library.cpp
Normal file
56
drm/1.0/vts/functional/shared_library.cpp
Normal file
@@ -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";
|
||||
}
|
||||
};
|
||||
41
drm/1.0/vts/functional/shared_library.h
Normal file
41
drm/1.0/vts/functional/shared_library.h
Normal file
@@ -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_
|
||||
BIN
drm/1.0/vts/functional/vendor/lib/libvtswidevine.so
vendored
Executable file
BIN
drm/1.0/vts/functional/vendor/lib/libvtswidevine.so
vendored
Executable file
Binary file not shown.
76
drm/1.0/vts/functional/vendor_modules.cpp
Normal file
76
drm/1.0/vts/functional/vendor_modules.cpp
Normal file
@@ -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)();
|
||||
}
|
||||
};
|
||||
63
drm/1.0/vts/functional/vendor_modules.h
Normal file
63
drm/1.0/vts/functional/vendor_modules.h
Normal file
@@ -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
|
||||
@@ -1,4 +1,5 @@
|
||||
// This is an autogenerated file, do not edit.
|
||||
subdirs = [
|
||||
"1.0",
|
||||
"1.0/vts/functional",
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user