diff options
Diffstat (limited to 'drm')
-rw-r--r-- | drm/1.0/Android.bp | 106 | ||||
-rw-r--r-- | drm/1.0/default/CryptoPlugin.cpp | 13 | ||||
-rw-r--r-- | drm/1.0/default/DrmFactory.cpp | 2 | ||||
-rw-r--r-- | drm/1.0/default/DrmPlugin.cpp | 1 | ||||
-rw-r--r-- | drm/1.0/default/LegacyPluginPath.cpp | 16 | ||||
-rw-r--r-- | drm/1.0/default/android.hardware.drm@1.0-service.rc | 2 | ||||
-rw-r--r-- | drm/1.0/default/include/PluginLoader.h | 5 | ||||
-rw-r--r-- | drm/1.0/vts/functional/drm_hal_clearkey_test.cpp | 64 | ||||
-rw-r--r-- | drm/1.0/vts/functional/drm_hal_vendor_test.cpp | 27 | ||||
-rw-r--r-- | drm/1.1/Android.bp | 28 | ||||
-rw-r--r-- | drm/1.1/ICryptoFactory.hal | 33 | ||||
-rw-r--r-- | drm/1.1/IDrmFactory.hal | 35 | ||||
-rw-r--r-- | drm/1.1/IDrmPlugin.hal | 244 | ||||
-rw-r--r-- | drm/1.1/types.hal | 219 | ||||
-rw-r--r-- | drm/1.1/vts/functional/Android.bp | 34 | ||||
-rw-r--r-- | drm/1.1/vts/functional/drm_hal_clearkey_test.cpp | 874 | ||||
-rw-r--r-- | drm/Android.bp | 6 |
17 files changed, 1568 insertions, 141 deletions
diff --git a/drm/1.0/Android.bp b/drm/1.0/Android.bp index d004b8272..aca5ae492 100644 --- a/drm/1.0/Android.bp +++ b/drm/1.0/Android.bp @@ -1,7 +1,11 @@ -// This file is autogenerated by hidl-gen. Do not edit manually. +// This file is autogenerated by hidl-gen -Landroidbp. -filegroup { - name: "android.hardware.drm@1.0_hal", +hidl_interface { + name: "android.hardware.drm@1.0", + root: "android.hardware", + vndk: { + enabled: true, + }, srcs: [ "types.hal", "ICryptoFactory.hal", @@ -10,85 +14,25 @@ filegroup { "IDrmPlugin.hal", "IDrmPluginListener.hal", ], -} - -genrule { - name: "android.hardware.drm@1.0_genc++", - tools: ["hidl-gen"], - cmd: "$(location hidl-gen) -o $(genDir) -Lc++-sources -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport android.hardware.drm@1.0", - srcs: [ - ":android.hardware.drm@1.0_hal", + interfaces: [ + "android.hidl.base@1.0", ], - out: [ - "android/hardware/drm/1.0/types.cpp", - "android/hardware/drm/1.0/CryptoFactoryAll.cpp", - "android/hardware/drm/1.0/CryptoPluginAll.cpp", - "android/hardware/drm/1.0/DrmFactoryAll.cpp", - "android/hardware/drm/1.0/DrmPluginAll.cpp", - "android/hardware/drm/1.0/DrmPluginListenerAll.cpp", + types: [ + "BufferType", + "DestinationBuffer", + "EventType", + "KeyRequestType", + "KeyStatus", + "KeyStatusType", + "KeyType", + "KeyValue", + "Mode", + "Pattern", + "SecureStop", + "SharedBuffer", + "Status", + "SubSample", ], + gen_java: false, } -genrule { - name: "android.hardware.drm@1.0_genc++_headers", - tools: ["hidl-gen"], - cmd: "$(location hidl-gen) -o $(genDir) -Lc++-headers -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport android.hardware.drm@1.0", - srcs: [ - ":android.hardware.drm@1.0_hal", - ], - out: [ - "android/hardware/drm/1.0/types.h", - "android/hardware/drm/1.0/hwtypes.h", - "android/hardware/drm/1.0/ICryptoFactory.h", - "android/hardware/drm/1.0/IHwCryptoFactory.h", - "android/hardware/drm/1.0/BnHwCryptoFactory.h", - "android/hardware/drm/1.0/BpHwCryptoFactory.h", - "android/hardware/drm/1.0/BsCryptoFactory.h", - "android/hardware/drm/1.0/ICryptoPlugin.h", - "android/hardware/drm/1.0/IHwCryptoPlugin.h", - "android/hardware/drm/1.0/BnHwCryptoPlugin.h", - "android/hardware/drm/1.0/BpHwCryptoPlugin.h", - "android/hardware/drm/1.0/BsCryptoPlugin.h", - "android/hardware/drm/1.0/IDrmFactory.h", - "android/hardware/drm/1.0/IHwDrmFactory.h", - "android/hardware/drm/1.0/BnHwDrmFactory.h", - "android/hardware/drm/1.0/BpHwDrmFactory.h", - "android/hardware/drm/1.0/BsDrmFactory.h", - "android/hardware/drm/1.0/IDrmPlugin.h", - "android/hardware/drm/1.0/IHwDrmPlugin.h", - "android/hardware/drm/1.0/BnHwDrmPlugin.h", - "android/hardware/drm/1.0/BpHwDrmPlugin.h", - "android/hardware/drm/1.0/BsDrmPlugin.h", - "android/hardware/drm/1.0/IDrmPluginListener.h", - "android/hardware/drm/1.0/IHwDrmPluginListener.h", - "android/hardware/drm/1.0/BnHwDrmPluginListener.h", - "android/hardware/drm/1.0/BpHwDrmPluginListener.h", - "android/hardware/drm/1.0/BsDrmPluginListener.h", - ], -} - -cc_library { - name: "android.hardware.drm@1.0", - defaults: ["hidl-module-defaults"], - generated_sources: ["android.hardware.drm@1.0_genc++"], - generated_headers: ["android.hardware.drm@1.0_genc++_headers"], - export_generated_headers: ["android.hardware.drm@1.0_genc++_headers"], - vendor_available: true, - vndk: { - enabled: true, - }, - shared_libs: [ - "libhidlbase", - "libhidltransport", - "libhwbinder", - "liblog", - "libutils", - "libcutils", - ], - export_shared_lib_headers: [ - "libhidlbase", - "libhidltransport", - "libhwbinder", - "libutils", - ], -} diff --git a/drm/1.0/default/CryptoPlugin.cpp b/drm/1.0/default/CryptoPlugin.cpp index b86260342..19666f53a 100644 --- a/drm/1.0/default/CryptoPlugin.cpp +++ b/drm/1.0/default/CryptoPlugin.cpp @@ -99,8 +99,8 @@ namespace implementation { legacyPattern.mEncryptBlocks = pattern.encryptBlocks; legacyPattern.mSkipBlocks = pattern.skipBlocks; - android::CryptoPlugin::SubSample *legacySubSamples = - new android::CryptoPlugin::SubSample[subSamples.size()]; + std::unique_ptr<android::CryptoPlugin::SubSample[]> legacySubSamples = + std::make_unique<android::CryptoPlugin::SubSample[]>(subSamples.size()); size_t destSize = 0; for (size_t i = 0; i < subSamples.size(); i++) { @@ -109,12 +109,10 @@ namespace implementation { uint32_t numBytesOfEncryptedData = subSamples[i].numBytesOfEncryptedData; legacySubSamples[i].mNumBytesOfEncryptedData = numBytesOfEncryptedData; if (__builtin_add_overflow(destSize, numBytesOfClearData, &destSize)) { - delete[] legacySubSamples; _hidl_cb(Status::BAD_VALUE, 0, "subsample clear size overflow"); return Void(); } if (__builtin_add_overflow(destSize, numBytesOfEncryptedData, &destSize)) { - delete[] legacySubSamples; _hidl_cb(Status::BAD_VALUE, 0, "subsample encrypted size overflow"); return Void(); } @@ -151,7 +149,6 @@ namespace implementation { } if (destSize > destBuffer.size) { - delete[] legacySubSamples; _hidl_cb(Status::BAD_VALUE, 0, "subsample sum too large"); return Void(); } @@ -160,7 +157,6 @@ namespace implementation { destPtr = static_cast<void *>(base + destination.nonsecureMemory.offset); } else if (destination.type == BufferType::NATIVE_HANDLE) { if (!secure) { - delete[] legacySubSamples; _hidl_cb(Status::BAD_VALUE, 0, "native handle destination must be secure"); return Void(); } @@ -168,16 +164,13 @@ namespace implementation { destination.secureMemory.getNativeHandle()); destPtr = static_cast<void *>(handle); } else { - delete[] legacySubSamples; _hidl_cb(Status::BAD_VALUE, 0, "invalid destination type"); return Void(); } ssize_t result = mLegacyPlugin->decrypt(secure, keyId.data(), iv.data(), - legacyMode, legacyPattern, srcPtr, legacySubSamples, + legacyMode, legacyPattern, srcPtr, legacySubSamples.get(), subSamples.size(), destPtr, &detailMessage); - delete[] legacySubSamples; - uint32_t status; uint32_t bytesWritten; diff --git a/drm/1.0/default/DrmFactory.cpp b/drm/1.0/default/DrmFactory.cpp index 7e5d998e4..05951d7c0 100644 --- a/drm/1.0/default/DrmFactory.cpp +++ b/drm/1.0/default/DrmFactory.cpp @@ -1,6 +1,6 @@ /* * Copyright (C) 2016 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 diff --git a/drm/1.0/default/DrmPlugin.cpp b/drm/1.0/default/DrmPlugin.cpp index 1695ef772..809f694f1 100644 --- a/drm/1.0/default/DrmPlugin.cpp +++ b/drm/1.0/default/DrmPlugin.cpp @@ -93,6 +93,7 @@ namespace implementation { requestType = KeyRequestType::RELEASE; break; case android::DrmPlugin::kKeyRequestType_Unknown: + default: requestType = KeyRequestType::UNKNOWN; break; } diff --git a/drm/1.0/default/LegacyPluginPath.cpp b/drm/1.0/default/LegacyPluginPath.cpp index 369059d2c..d0a8f90a7 100644 --- a/drm/1.0/default/LegacyPluginPath.cpp +++ b/drm/1.0/default/LegacyPluginPath.cpp @@ -16,6 +16,8 @@ #include "LegacyPluginPath.h" +#include <unistd.h> + #include <cutils/properties.h> namespace android { @@ -24,12 +26,16 @@ namespace drm { namespace V1_0 { namespace implementation { +// 64-bit DRM depends on OEM libraries that aren't +// provided for all devices. If the drm hal service +// is running as 64-bit use the 64-bit libs, otherwise +// use the 32-bit libs. const char* getDrmPluginPath() { - if (property_get_bool("drm.64bit.enabled", false)) { - return "/vendor/lib64/mediadrm"; - } else { - return "/vendor/lib/mediadrm"; - } +#if defined(__LP64__) + return "/vendor/lib64/mediadrm"; +#else + return "/vendor/lib/mediadrm"; +#endif } } // namespace implementation diff --git a/drm/1.0/default/android.hardware.drm@1.0-service.rc b/drm/1.0/default/android.hardware.drm@1.0-service.rc index e7beca35e..a3457b523 100644 --- a/drm/1.0/default/android.hardware.drm@1.0-service.rc +++ b/drm/1.0/default/android.hardware.drm@1.0-service.rc @@ -1,4 +1,4 @@ -service drm-hal-1-0 /vendor/bin/hw/android.hardware.drm@1.0-service +service vendor.drm-hal-1-0 /vendor/bin/hw/android.hardware.drm@1.0-service class hal user media group mediadrm drmrpc diff --git a/drm/1.0/default/include/PluginLoader.h b/drm/1.0/default/include/PluginLoader.h index f387b3cbc..0c45fb3ef 100644 --- a/drm/1.0/default/include/PluginLoader.h +++ b/drm/1.0/default/include/PluginLoader.h @@ -85,7 +85,10 @@ class PluginLoader { libraries.push(library); T* result = createFactoryFunc(); return result; - } + } else { + ALOGE("Failed to lookup symbol %s in library %s: %s", + entry, path, library->lastError()); + } } return NULL; } diff --git a/drm/1.0/vts/functional/drm_hal_clearkey_test.cpp b/drm/1.0/vts/functional/drm_hal_clearkey_test.cpp index a110eb150..4a1892b8e 100644 --- a/drm/1.0/vts/functional/drm_hal_clearkey_test.cpp +++ b/drm/1.0/vts/functional/drm_hal_clearkey_test.cpp @@ -89,10 +89,6 @@ static const uint8_t kInvalidUUID[16] = { 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80}; -static const uint32_t k256SubSampleByteCount = 256; -static const uint32_t k512SubSampleClearBytes = 512; -static const uint32_t k512SubSampleEncryptedBytes = 512; - class DrmHalClearkeyFactoryTest : public ::testing::VtsHalHidlTargetTestBase { public: virtual void SetUp() override { @@ -349,8 +345,7 @@ SessionId DrmHalClearkeyPluginTest::openSession() { * Helper method to close a session */ void DrmHalClearkeyPluginTest::closeSession(const SessionId& sessionId) { - auto result = drmPlugin->closeSession(sessionId); - EXPECT_EQ(Status::OK, result); + EXPECT_TRUE(drmPlugin->closeSession(sessionId).isOk()); } /** @@ -793,7 +788,7 @@ TEST_F(DrmHalClearkeyPluginTest, SetMacAlgorithmNoSession) { */ 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 = std::vector<uint8_t>(AES_BLOCK_SIZE, 0); @@ -822,7 +817,7 @@ TEST_F(DrmHalClearkeyPluginTest, GenericDecryptNotSupported) { 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, @@ -836,7 +831,7 @@ TEST_F(DrmHalClearkeyPluginTest, GenericSignNotSupported) { 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, @@ -926,8 +921,7 @@ sp<IMemory> DrmHalClearkeyPluginTest::getDecryptMemory(size_t size, */ TEST_F(DrmHalClearkeyPluginTest, SetMediaDrmSession) { auto sessionId = openSession(); - Status status = cryptoPlugin->setMediaDrmSession(sessionId); - EXPECT_EQ(Status::OK, status); + EXPECT_TRUE(cryptoPlugin->setMediaDrmSession(sessionId).isOk()); closeSession(sessionId); } @@ -948,8 +942,7 @@ TEST_F(DrmHalClearkeyPluginTest, SetMediaDrmSessionClosedSession) { */ TEST_F(DrmHalClearkeyPluginTest, SetMediaDrmSessionEmptySession) { SessionId sessionId; - Status status = cryptoPlugin->setMediaDrmSession(sessionId); - EXPECT_EQ(Status::OK, status); + EXPECT_TRUE(cryptoPlugin->setMediaDrmSession(sessionId).isOk()); } /** @@ -1131,18 +1124,17 @@ TEST_F(DrmHalClearkeyDecryptTest, TestQueryKeyStatus) { TEST_F(DrmHalClearkeyDecryptTest, ClearSegmentTest) { vector<uint8_t> iv(AES_BLOCK_SIZE, 0); const Pattern noPattern = {0, 0}; + const uint32_t kByteCount = 256; const vector<SubSample> subSamples = { - {.numBytesOfClearData = k256SubSampleByteCount, + {.numBytesOfClearData = kByteCount, .numBytesOfEncryptedData = 0}}; auto sessionId = openSession(); loadKeys(sessionId); - - Status status = cryptoPlugin->setMediaDrmSession(sessionId); - EXPECT_EQ(Status::OK, status); + EXPECT_TRUE(cryptoPlugin->setMediaDrmSession(sessionId).isOk()); uint32_t byteCount = decrypt(Mode::UNENCRYPTED, &iv[0], subSamples, noPattern, Status::OK); - EXPECT_EQ(k256SubSampleByteCount, byteCount); + EXPECT_EQ(kByteCount, byteCount); closeSession(sessionId); } @@ -1154,18 +1146,18 @@ TEST_F(DrmHalClearkeyDecryptTest, ClearSegmentTest) { TEST_F(DrmHalClearkeyDecryptTest, EncryptedAesCtrSegmentTest) { vector<uint8_t> iv(AES_BLOCK_SIZE, 0); const Pattern noPattern = {0, 0}; + const uint32_t kClearBytes = 512; + const uint32_t kEncryptedBytes = 512; const vector<SubSample> subSamples = { - {.numBytesOfClearData = k512SubSampleClearBytes, - .numBytesOfEncryptedData = k512SubSampleEncryptedBytes}}; + {.numBytesOfClearData = kClearBytes, + .numBytesOfEncryptedData = kEncryptedBytes}}; auto sessionId = openSession(); loadKeys(sessionId); - - Status status = cryptoPlugin->setMediaDrmSession(sessionId); - EXPECT_EQ(Status::OK, status); + EXPECT_TRUE(cryptoPlugin->setMediaDrmSession(sessionId).isOk()); uint32_t byteCount = decrypt(Mode::AES_CTR, &iv[0], subSamples, noPattern, Status::OK); - EXPECT_EQ(k512SubSampleClearBytes + k512SubSampleEncryptedBytes, byteCount); + EXPECT_EQ(kClearBytes + kEncryptedBytes, byteCount); closeSession(sessionId); } @@ -1177,12 +1169,10 @@ TEST_F(DrmHalClearkeyDecryptTest, EncryptedAesCtrSegmentTestNoKeys) { vector<uint8_t> iv(AES_BLOCK_SIZE, 0); const Pattern noPattern = {0, 0}; const vector<SubSample> subSamples = { - {.numBytesOfClearData = k256SubSampleByteCount, - .numBytesOfEncryptedData = k256SubSampleByteCount}}; + {.numBytesOfClearData = 256, + .numBytesOfEncryptedData = 256}}; auto sessionId = openSession(); - - Status status = cryptoPlugin->setMediaDrmSession(sessionId); - EXPECT_EQ(Status::OK, status); + EXPECT_TRUE(cryptoPlugin->setMediaDrmSession(sessionId).isOk()); uint32_t byteCount = decrypt(Mode::AES_CTR, &iv[0], subSamples, noPattern, Status::ERROR_DRM_NO_LICENSE); @@ -1207,9 +1197,9 @@ void DrmHalClearkeyDecryptTest::decryptWithInvalidKeys( EXPECT_EQ(Status::OK, status); EXPECT_EQ(0u, myKeySetId.size()); }); - ASSERT_OK(res); + EXPECT_OK(res); - ASSERT_TRUE(cryptoPlugin->setMediaDrmSession(sessionId).isOk()); + EXPECT_TRUE(cryptoPlugin->setMediaDrmSession(sessionId).isOk()); uint32_t byteCount = decrypt(Mode::AES_CTR, &iv[0], subSamples, noPattern, Status::ERROR_DRM_NO_LICENSE); @@ -1224,9 +1214,11 @@ void DrmHalClearkeyDecryptTest::decryptWithInvalidKeys( TEST_F(DrmHalClearkeyDecryptTest, DecryptWithEmptyKey) { vector<uint8_t> iv(AES_BLOCK_SIZE, 0); const Pattern noPattern = {0, 0}; + const uint32_t kClearBytes = 512; + const uint32_t kEncryptedBytes = 512; const vector<SubSample> subSamples = { - {.numBytesOfClearData = k512SubSampleClearBytes, - .numBytesOfEncryptedData = k512SubSampleEncryptedBytes}}; + {.numBytesOfClearData = kClearBytes, + .numBytesOfEncryptedData = kEncryptedBytes}}; // base 64 encoded JSON response string, must not contain padding character '=' const hidl_string emptyKeyResponse = @@ -1259,9 +1251,11 @@ TEST_F(DrmHalClearkeyDecryptTest, DecryptWithEmptyKey) { TEST_F(DrmHalClearkeyDecryptTest, DecryptWithKeyTooLong) { vector<uint8_t> iv(AES_BLOCK_SIZE, 0); const Pattern noPattern = {0, 0}; + const uint32_t kClearBytes = 512; + const uint32_t kEncryptedBytes = 512; const vector<SubSample> subSamples = { - {.numBytesOfClearData = k512SubSampleClearBytes, - .numBytesOfEncryptedData = k512SubSampleEncryptedBytes}}; + {.numBytesOfClearData = kClearBytes, + .numBytesOfEncryptedData = kEncryptedBytes}}; // base 64 encoded JSON response string, must not contain padding character '=' const hidl_string keyTooLongResponse = diff --git a/drm/1.0/vts/functional/drm_hal_vendor_test.cpp b/drm/1.0/vts/functional/drm_hal_vendor_test.cpp index 47c6950cd..d03b2af37 100644 --- a/drm/1.0/vts/functional/drm_hal_vendor_test.cpp +++ b/drm/1.0/vts/functional/drm_hal_vendor_test.cpp @@ -97,6 +97,27 @@ static const uint8_t kInvalidUUID[16] = { static drm_vts::VendorModules* gVendorModules = nullptr; +// Test environment for drm +class DrmHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase { + public: + // get the test environment singleton + static DrmHidlEnvironment* Instance() { + static DrmHidlEnvironment* instance = new DrmHidlEnvironment; + return instance; + } + + void registerTestServices() override { + registerTestService<ICryptoFactory>(); + registerTestService<IDrmFactory>(); + setServiceCombMode(::testing::HalServiceCombMode::NO_COMBINATION); + } + + private: + DrmHidlEnvironment() {} + + GTEST_DISALLOW_COPY_AND_ASSIGN_(DrmHidlEnvironment); +}; + class DrmHalVendorFactoryTest : public testing::TestWithParam<std::string> { public: DrmHalVendorFactoryTest() @@ -1598,6 +1619,10 @@ int main(int argc, char** argv) { std::cerr << "WARNING: No vendor modules found in " << kModulePath << ", all vendor tests will be skipped" << std::endl; } + ::testing::AddGlobalTestEnvironment(DrmHidlEnvironment::Instance()); ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); + DrmHidlEnvironment::Instance()->init(&argc, argv); + int status = RUN_ALL_TESTS(); + ALOGI("Test result = %d", status); + return status; } diff --git a/drm/1.1/Android.bp b/drm/1.1/Android.bp new file mode 100644 index 000000000..dba3e427a --- /dev/null +++ b/drm/1.1/Android.bp @@ -0,0 +1,28 @@ +// This file is autogenerated by hidl-gen -Landroidbp. + +hidl_interface { + name: "android.hardware.drm@1.1", + root: "android.hardware", + vndk: { + enabled: true, + }, + srcs: [ + "types.hal", + "ICryptoFactory.hal", + "IDrmFactory.hal", + "IDrmPlugin.hal", + ], + interfaces: [ + "android.hardware.drm@1.0", + "android.hidl.base@1.0", + ], + types: [ + "DrmMetricGroup", + "HdcpLevel", + "KeyRequestType", + "SecureStopRelease", + "SecurityLevel", + ], + gen_java: false, +} + diff --git a/drm/1.1/ICryptoFactory.hal b/drm/1.1/ICryptoFactory.hal new file mode 100644 index 000000000..1f8caa57d --- /dev/null +++ b/drm/1.1/ICryptoFactory.hal @@ -0,0 +1,33 @@ +/* + * 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. + */ +package android.hardware.drm@1.1; + +import @1.0::ICryptoFactory; +import @1.0::ICryptoPlugin; + +/** + * ICryptoFactory is the main entry point for interacting with a vendor's + * crypto HAL to create crypto plugins. Crypto plugins create crypto sessions + * which are used by a codec to decrypt protected video content. + * + * The 1.1 factory must always create 1.0 ICryptoPlugin interfaces, which are + * returned via the 1.0 createPlugin method. + * + * The ICryptoFactory hal is required because all top-level interfaces + * have to be updated in a minor uprev. + */ +interface ICryptoFactory extends @1.0::ICryptoFactory { +}; diff --git a/drm/1.1/IDrmFactory.hal b/drm/1.1/IDrmFactory.hal new file mode 100644 index 000000000..b6d6bfb3d --- /dev/null +++ b/drm/1.1/IDrmFactory.hal @@ -0,0 +1,35 @@ +/* + * 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. + */ +package android.hardware.drm@1.1; + +import @1.0::IDrmFactory; +import @1.0::IDrmPlugin; + +/** + * IDrmFactory is the main entry point for interacting with a vendor's + * drm HAL to create drm plugin instances. A drm plugin instance + * creates drm sessions which are used to obtain keys for a crypto + * session so it can decrypt protected video content. + * + * The 1.1 factory must always create 1.1 IDrmPlugin interfaces, which are + * returned via the 1.0 createPlugin method. + * + * To use 1.1 features the caller must cast the returned interface to a + * 1.1 HAL, using V1_1::IDrmPlugin::castFrom(). + */ + +interface IDrmFactory extends @1.0::IDrmFactory { +}; diff --git a/drm/1.1/IDrmPlugin.hal b/drm/1.1/IDrmPlugin.hal new file mode 100644 index 000000000..2980dc2ab --- /dev/null +++ b/drm/1.1/IDrmPlugin.hal @@ -0,0 +1,244 @@ +/** + * 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. + */ +package android.hardware.drm@1.1; + +import @1.0::IDrmPlugin; +import @1.0::IDrmPluginListener; +import @1.0::KeyedVector; +import @1.0::KeyType; +import @1.0::Status; +import @1.1::DrmMetricGroup; +import @1.1::HdcpLevel; +import @1.1::KeyRequestType; +import @1.0::SecureStopId; +import @1.1::SecureStopRelease; +import @1.1::SecurityLevel; +import @1.0::SessionId; + +/** + * IDrmPlugin is used to interact with a specific drm plugin that was created by + * IDrm::createPlugin. A drm plugin provides methods for obtaining drm keys that + * may be used by a codec to decrypt protected video content. + */ +interface IDrmPlugin extends @1.0::IDrmPlugin { + /** + * Open a new session at a requested security level. The security level + * represents the robustness of the device's DRM implementation. By default, + * sessions are opened at the native security level of the device which is + * the maximum level that can be supported. Overriding the security level is + * necessary when the decrypted frames need to be manipulated, such as for + * image compositing. The security level parameter must be equal to or lower + * than the native level. If the requested level is not supported, the next + * lower supported security level must be set. The level can be queried + * using {@link #getSecurityLevel}. A session ID is returned. When the + * drm@1.0 openSession is called, which has no securityLevel parameter, the + * security level is defaulted to the native security level of the device. + * + * @return status the status of the call. The status must be OK or one of + * the following errors: ERROR_DRM_NOT_PROVISIONED if the device + * requires provisioning before it can open a session, + * ERROR_DRM_RESOURCE_BUSY if there are insufficent resources available + * to open a session, ERROR_DRM_CANNOT_HANDLE if the requested security + * level is higher than the native level or lower than the lowest + * supported level or if openSession is not supported at the time of + * the call, or ERROR_DRM_INVALID_STATE if the HAL is in a state where + * a session cannot be opened. + * @param level the requested security level + * @return sessionId the session ID for the newly opened session + */ + openSession_1_1(SecurityLevel securityLevel) generates (Status status, + SessionId sessionId); + + /** + * A key request/response exchange occurs between the app and a License + * Server to obtain the keys required to decrypt the content. + * getKeyRequest_1_1() is used to obtain an opaque key request blob that is + * delivered to the license server. + * + * getKeyRequest_1_1() only differs from getKeyRequest() in that additional + * values are returned in 1.1::KeyRequestType as compared to + * 1.0::KeyRequestType + * + * @param scope may be a sessionId or a keySetId, depending on the + * specified keyType. When the keyType is OFFLINE or STREAMING, + * scope should be set to the sessionId the keys will be provided + * to. When the keyType is RELEASE, scope should be set to the + * keySetId of the keys being released. + * @param initData container-specific data, its meaning is interpreted + * based on the mime type provided in the mimeType parameter. + * It could contain, for example, the content ID, key ID or + * other data obtained from the content metadata that is + * required to generate the key request. initData may be empty + * when keyType is RELEASE. + * @param mimeType identifies the mime type of the content + * @param keyType specifies if the keys are to be used for streaming, + * offline or a release + * @param optionalParameters included in the key request message to + * allow a client application to provide additional message + * parameters to the server. + * @return status the status of the call. The status must be OK or one of + * the following errors: ERROR_DRM_SESSION_NOT_OPENED if the + * session is not opened, ERROR_DRM_NOT_PROVISIONED if the device + * requires provisioning before it can generate a key request, + * ERROR_DRM_CANNOT_HANDLE if getKeyRequest is not supported + * at the time of the call, BAD_VALUE if any parameters are + * invalid or ERROR_DRM_INVALID_STATE if the HAL is in a + * state where a key request cannot be generated. + * @return request if successful, the opaque key request blob is returned + * @return requestType indicates type information about the returned + * request. The type may be one of INITIAL, RENEWAL, RELEASE, + * NONE or UPDATE. An INITIAL request is the first key request + * for a license. RENEWAL is a subsequent key request used to + * refresh the keys in a license. RELEASE corresponds to a + * keyType of RELEASE, which indicates keys are being released. + * NONE indicates that no request is needed because the keys are + * already loaded. UPDATE indicates that the keys need to be + * refetched after the initial license request. + * @return defaultUrl the URL that the request may be sent to, if + * provided by the drm HAL. The app may choose to override this URL. + */ + getKeyRequest_1_1(vec<uint8_t> scope, vec<uint8_t> initData, + string mimeType, KeyType keyType, KeyedVector optionalParameters) + generates (Status status, vec<uint8_t> request, + KeyRequestType requestType, string defaultUrl); + + /** + * Return the currently negotiated and max supported HDCP levels. + * + * The current level is based on the display(s) the device is connected to. + * If multiple HDCP-capable displays are simultaneously connected to + * separate interfaces, this method returns the lowest negotiated HDCP level + * of all interfaces. + * + * The maximum HDCP level is the highest level that can potentially be + * negotiated. It is a constant for any device, i.e. it does not depend on + * downstream receiving devices that could be connected. For example, if + * the device has HDCP 1.x keys and is capable of negotiating HDCP 1.x, but + * does not have HDCP 2.x keys, then the maximum HDCP capability would be + * reported as 1.x. If multiple HDCP-capable interfaces are present, it + * indicates the highest of the maximum HDCP levels of all interfaces. + * + * This method should only be used for informational purposes, not for + * enforcing compliance with HDCP requirements. Trusted enforcement of HDCP + * policies must be handled by the DRM system. + * + * @return status the status of the call. The status must be OK or + * ERROR_DRM_INVALID_STATE if the HAL is in a state where the HDCP + * level cannot be queried. + * @return connectedLevel the lowest HDCP level for any connected + * displays + * @return maxLevel the highest HDCP level that can be supported + * by the device + */ + getHdcpLevels() generates (Status status, HdcpLevel connectedLevel, + HdcpLevel maxLevel); + + /** + * Return the current number of open sessions and the maximum number of + * sessions that may be opened simultaneosly among all DRM instances for the + * active DRM scheme. + * + * @return status the status of the call. The status must be OK or + * ERROR_DRM_INVALID_STATE if the HAL is in a state where number of + * sessions cannot be queried. + * @return currentSessions the number of currently opened sessions + * @return maxSessions the maximum number of sessions that the device + * can support + */ + getNumberOfSessions() generates (Status status, uint32_t currentSessions, + uint32_t maxSessions); + + /** + * Return the current security level of a session. A session has an initial + * security level determined by the robustness of the DRM system's + * implementation on the device. + * + * @param sessionId the session id the call applies to + * @return status the status of the call. The status must be OK or one of + * the following errors: ERROR_DRM_SESSION_NOT_OPENED if the + * session is not opened, BAD_VALUE if the sessionId is invalid or + * ERROR_DRM_INVALID_STATE if the HAL is in a state where the + * security level cannot be queried. + * @return level the current security level for the session + */ + getSecurityLevel(vec<uint8_t> sessionId) generates(Status status, + SecurityLevel level); + + /** + * Returns the plugin-specific metrics. Multiple metric groups may be + * returned in one call to getMetrics(). The scope and definition of the + * metrics is defined by the plugin. + * + * @return status the status of the call. The status must be OK or + * ERROR_DRM_INVALID_STATE if the metrics are not available to be + * returned. + * @return metric_groups the collection of metric groups provided by the + * plugin. + */ + getMetrics() generates (Status status, vec<DrmMetricGroup> metric_groups); + + /** + * Get the IDs of all secure stops on the device + * + * @return status the status of the call. The status must be OK or + * ERROR_DRM_INVALID_STATE if the HAL is in a state where the secure stop + * IDs cannot be returned. + * @return secureStopIds a list of the IDs + */ + getSecureStopIds() generates + (Status status, vec<SecureStopId> secureStopIds); + + /** + * Release secure stops given a release message from the key server + * + * @param ssRelease the secure stop release message identifying one or more + * secure stops to release. ssRelease is opaque, it is passed directly from + * a DRM license server through the app and media framework to the vendor + * HAL module. The format and content of ssRelease must be defined by the + * DRM scheme being implemented according to this HAL. The DRM scheme + * can be identified by its UUID which can be queried using + * IDrmFactory::isCryptoSchemeSupported. + * + * @return status the status of the call. The status must be OK or one of + * the following errors: BAD_VALUE if ssRelease is invalid or + * ERROR_DRM_INVALID_STATE if the HAL is in a state where the secure stop + * cannot be released. + */ + releaseSecureStops(SecureStopRelease ssRelease) generates (Status status); + + /** + * Remove a secure stop given its secure stop ID, without requiring + * a secure stop release response message from the key server. + * + * @param secureStopId the ID of the secure stop to release. + * + * @return status the status of the call. The status must be OK or one of + * the following errors: BAD_VALUE if the secureStopId is invalid or + * ERROR_DRM_INVALID_STATE if the HAL is in a state where the secure stop + * cannot be released. + */ + removeSecureStop(SecureStopId secureStopId) generates (Status status); + + /** + * Remove all secure stops on the device without requiring a secure + * stop release response message from the key server. + * + * @return status the status of the call. The status must be OK or + * ERROR_DRM_INVALID_STATE if the HAL is in a state where the secure + * stops cannot be removed. + */ + removeAllSecureStops() generates (Status status); +}; diff --git a/drm/1.1/types.hal b/drm/1.1/types.hal new file mode 100644 index 000000000..015f1b7fc --- /dev/null +++ b/drm/1.1/types.hal @@ -0,0 +1,219 @@ +/** + * 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. + */ + +package android.hardware.drm@1.1; + +import @1.0::KeyRequestType; + +/** + * This message contains plugin-specific metrics made available to the client. + * The message is used for making vendor-specific metrics available to an + * application. The framework is not consuming any of the information. + * + * Metrics are grouped in instances of DrmMetricGroup. Each group contains + * multiple instances of Metric. + * + * Example: + * + * Capture the timing information of a buffer copy event, "buf_copy", broken + * out by the "size" of the buffer. + * + * DrmMetricGroup { + * metrics[0] { + * name: "buf_copy" + * attributes[0] { + * name: "size" + * type: INT64_TYPE + * int64Value: 1024 + * } + * values[0] { + * componentName: "operation_count" + * type: INT64_TYPE + * int64Value: 75 + * } + * values[1] { + * component_name: "average_time_seconds" + * type: DOUBLE_TYPE + * doubleValue: 0.00000042 + * } + * } + * } + */ +struct DrmMetricGroup { + /** + * Used to discriminate the type of value being stored in the structs + * below. + */ + enum ValueType : uint8_t { + INT64_TYPE, + DOUBLE_TYPE, + STRING_TYPE, + }; + + /** + * A detail about the metric being captured. The fields of an Attribute + * are opaque to the framework. + */ + struct Attribute { + string name; + /** + * The type field indicates which of the following values is used. + */ + ValueType type; + int64_t int64Value; + double doubleValue; + string stringValue; + }; + + /** + * A value of the metric. A metric may have multiple values. The + * component name may be left empty if there is only supposed to be + * one value for the given metric. The fields of the Value are + * opaque to the framework. + */ + struct Value { + string componentName; + /** + * The type field indicates which of the following values is used. + */ + ValueType type; + int64_t int64Value; + double doubleValue; + string stringValue; + }; + + /** + * The metric being captured. A metric must have a name and at least one + * value. A metric may have 0 or more attributes. The fields of a Metric + * are opaque to the framework. + */ + struct Metric { + string name; + vec<Attribute> attributes; + // A Metric may have one or more values. Multiple values are useful + // for capturing different aspects of the same metric. E.g. capture + // the min, max, average, count, and stdev of a particular metric. + vec<Value> values; + }; + + /** + * The list of metrics to be captured. + */ + vec<Metric> metrics; +}; + +/** + * HDCP specifications are defined by Digital Content Protection LLC (DCP). + * "HDCP Specification Rev. 2.2 Interface Independent Adaptation" + * "HDCP 2.2 on HDMI Specification" + */ +enum HdcpLevel : uint32_t { + /** + * Unable to determine the HDCP level + */ + HDCP_UNKNOWN, + + /** + * No HDCP, output is unprotected + */ + HDCP_NONE, + + /** + * HDCP version 1.0 + */ + HDCP_V1, + + /** + * HDCP version 2.0 Type 1. + */ + HDCP_V2, + + /** + * HDCP version 2.1 Type 1. + */ + HDCP_V2_1, + + /** + * HDCP version 2.2 Type 1. + */ + HDCP_V2_2, + + /** + * No digital output, implicitly secure + */ + HDCP_NO_OUTPUT +}; + +/** + * KeyRequestTypes (in addition to those from 1.0) which allow an app + * to determine the type of a key request returned from getKeyRequest. + */ +enum KeyRequestType : @1.0::KeyRequestType { + /** + * Keys are already loaded. No key request is needed. + */ + NONE, + + /** + * Keys have previously been loaded. An additional (non-renewal) license + * request is needed. + */ + UPDATE, +}; + +enum SecurityLevel : uint32_t { + /** + * Unable to determine the security level + */ + UNKNOWN, + + /** + * Software-based whitebox crypto + */ + SW_SECURE_CRYPTO, + + /** + * Software-based whitebox crypto and an obfuscated decoder + */ + SW_SECURE_DECODE, + + /** + * DRM key management and crypto operations are performed within a + * hardware backed trusted execution environment + */ + HW_SECURE_CRYPTO, + + /** + * DRM key management, crypto operations and decoding of content + * are performed within a hardware backed trusted execution environment + */ + HW_SECURE_DECODE, + + /** + * DRM key management, crypto operations, decoding of content and all + * handling of the media (compressed and uncompressed) is handled within + * a hardware backed trusted execution environment. + */ + HW_SECURE_ALL, +}; + +/** + * Encapsulates a secure stop release opaque object + */ +struct SecureStopRelease { + vec<uint8_t> opaqueData; +}; + diff --git a/drm/1.1/vts/functional/Android.bp b/drm/1.1/vts/functional/Android.bp new file mode 100644 index 000000000..782f2833c --- /dev/null +++ b/drm/1.1/vts/functional/Android.bp @@ -0,0 +1,34 @@ +// +// 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: "VtsHalDrmV1_1TargetTest", + defaults: ["VtsHalTargetTestDefaults"], + srcs: [ + "drm_hal_clearkey_test.cpp" + ], + static_libs: [ + "android.hardware.drm@1.0", + "android.hardware.drm@1.1", + "android.hardware.drm@1.0-helper", + "android.hidl.allocator@1.0", + "android.hidl.memory@1.0", + "libhidlmemory", + "libnativehelper", + "libssl", + "libcrypto", + ], +} diff --git a/drm/1.1/vts/functional/drm_hal_clearkey_test.cpp b/drm/1.1/vts/functional/drm_hal_clearkey_test.cpp new file mode 100644 index 000000000..124661696 --- /dev/null +++ b/drm/1.1/vts/functional/drm_hal_clearkey_test.cpp @@ -0,0 +1,874 @@ +/* + * 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.1" + +#include <android/hardware/drm/1.1/ICryptoFactory.h> +#include <android/hardware/drm/1.0/ICryptoPlugin.h> +#include <android/hardware/drm/1.1/IDrmFactory.h> +#include <android/hardware/drm/1.0/IDrmPlugin.h> +#include <android/hardware/drm/1.1/IDrmPlugin.h> +#include <android/hardware/drm/1.0/types.h> +#include <android/hardware/drm/1.1/types.h> +#include <android/hidl/allocator/1.0/IAllocator.h> +#include <android/hidl/manager/1.0/IServiceManager.h> +#include <gtest/gtest.h> +#include <hidl/HidlSupport.h> +#include <hidl/ServiceManagement.h> +#include <hidlmemory/mapping.h> +#include <log/log.h> +#include <memory> +#include <openssl/aes.h> +#include <random> + +#include "VtsHalHidlTargetTestBase.h" +#include "VtsHalHidlTargetTestEnvBase.h" + +namespace drm = ::android::hardware::drm; +using ::android::hardware::drm::V1_0::BufferType; +using ::android::hardware::drm::V1_0::DestinationBuffer; +using ::android::hardware::drm::V1_0::ICryptoPlugin; +using ::android::hardware::drm::V1_0::KeyedVector; +using ::android::hardware::drm::V1_0::KeyValue; +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::drm::V1_0::SubSample; + +using ::android::hardware::drm::V1_1::DrmMetricGroup; +using ::android::hardware::drm::V1_1::HdcpLevel; +using ::android::hardware::drm::V1_1::ICryptoFactory; +using ::android::hardware::drm::V1_1::IDrmFactory; +using ::android::hardware::drm::V1_1::IDrmPlugin; +using ::android::hardware::drm::V1_1::KeyRequestType; +using ::android::hardware::drm::V1_1::SecureStopRelease; +using ::android::hardware::drm::V1_1::SecurityLevel; +using ::android::hardware::drm::V1_1::SecurityLevel; + +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()) + +// To be used in mpd to specify drm scheme for players +static const uint8_t kClearKeyUUID[16] = { + 0xE2, 0x71, 0x9D, 0x58, 0xA9, 0x85, 0xB3, 0xC9, + 0x78, 0x1A, 0xB0, 0x30, 0xAF, 0x78, 0xD3, 0x0E}; + +// Test environment for drm +class DrmHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase { + public: + // get the test environment singleton + static DrmHidlEnvironment* Instance() { + static DrmHidlEnvironment* instance = new DrmHidlEnvironment; + return instance; + } + + virtual void HidlSetUp() override { ALOGI("SetUp DrmHidlEnvironment"); } + + virtual void HidlTearDown() override { ALOGI("TearDown DrmHidlEnvironment"); } + + void registerTestServices() override { + registerTestService<ICryptoFactory>(); + registerTestService<IDrmFactory>(); + setServiceCombMode(::testing::HalServiceCombMode::NO_COMBINATION); + } + + private: + DrmHidlEnvironment() {} + + GTEST_DISALLOW_COPY_AND_ASSIGN_(DrmHidlEnvironment); +}; + + +class DrmHalClearkeyTest : public ::testing::VtsHalHidlTargetTestBase { +public: + virtual void SetUp() override { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + + ALOGD("DrmHalClearkeyTest: Running test %s.%s", test_info->test_case_name(), + test_info->name()); + + auto manager = android::hardware::defaultServiceManager(); + ASSERT_NE(nullptr, manager.get()); + manager->listByInterface(IDrmFactory::descriptor, + [&](const hidl_vec<hidl_string> ®istered) { + for (const auto &instance : registered) { + sp<IDrmFactory> drmFactory = + ::testing::VtsHalHidlTargetTestBase::getService<IDrmFactory>(instance); + drmPlugin = createDrmPlugin(drmFactory); + if (drmPlugin != nullptr) { + break; + } + } + } + ); + + manager->listByInterface(ICryptoFactory::descriptor, + [&](const hidl_vec<hidl_string> ®istered) { + for (const auto &instance : registered) { + sp<ICryptoFactory> cryptoFactory = + ::testing::VtsHalHidlTargetTestBase::getService<ICryptoFactory>(instance); + cryptoPlugin = createCryptoPlugin(cryptoFactory); + if (cryptoPlugin != nullptr) { + break; + } + } + } + ); + + ASSERT_NE(nullptr, drmPlugin.get()) << "Can't find clearkey drm@1.1 plugin"; + ASSERT_NE(nullptr, cryptoPlugin.get()) << "Can't find clearkey crypto@1.1 plugin"; + } + + + virtual void TearDown() override {} + + SessionId openSession(); + SessionId openSession(SecurityLevel level); + void closeSession(const SessionId& sessionId); + hidl_vec<uint8_t> loadKeys(const SessionId& sessionId, const KeyType& type); + + private: + sp<IDrmPlugin> createDrmPlugin(sp<IDrmFactory> drmFactory) { + if (drmFactory == nullptr) { + return nullptr; + } + sp<IDrmPlugin> plugin = nullptr; + auto res = drmFactory->createPlugin( + kClearKeyUUID, "", + [&](Status status, const sp<drm::V1_0::IDrmPlugin>& pluginV1_0) { + EXPECT_EQ(Status::OK, status); + plugin = IDrmPlugin::castFrom(pluginV1_0); + }); + + if (!res.isOk()) { + ALOGE("createDrmPlugin remote call failed"); + } + return plugin; + } + + sp<ICryptoPlugin> createCryptoPlugin(sp<ICryptoFactory> cryptoFactory) { + if (cryptoFactory == nullptr) { + return nullptr; + } + sp<ICryptoPlugin> plugin = nullptr; + hidl_vec<uint8_t> initVec; + auto res = cryptoFactory->createPlugin( + kClearKeyUUID, initVec, + [&](Status status, const sp<drm::V1_0::ICryptoPlugin>& pluginV1_0) { + EXPECT_EQ(Status::OK, status); + plugin = pluginV1_0; + }); + if (!res.isOk()) { + ALOGE("createCryptoPlugin remote call failed"); + } + return plugin; + } + +protected: + template <typename CT> + bool ValueEquals(DrmMetricGroup::ValueType type, const std::string& expected, const CT& actual) { + return type == DrmMetricGroup::ValueType::STRING_TYPE && expected == actual.stringValue; + } + + template <typename CT> + bool ValueEquals(DrmMetricGroup::ValueType type, const int64_t expected, const CT& actual) { + return type == DrmMetricGroup::ValueType::INT64_TYPE && expected == actual.int64Value; + } + + template <typename CT> + bool ValueEquals(DrmMetricGroup::ValueType type, const double expected, const CT& actual) { + return type == DrmMetricGroup::ValueType::DOUBLE_TYPE && expected == actual.doubleValue; + } + + template <typename AT, typename VT> + bool ValidateMetricAttributeAndValue(const DrmMetricGroup::Metric& metric, + const std::string& attributeName, const AT& attributeValue, + const std::string& componentName, const VT& componentValue) { + bool validAttribute = false; + bool validComponent = false; + for (DrmMetricGroup::Attribute attribute : metric.attributes) { + if (attribute.name == attributeName && + ValueEquals(attribute.type, attributeValue, attribute)) { + validAttribute = true; + } + } + for (DrmMetricGroup::Value value : metric.values) { + if (value.componentName == componentName && + ValueEquals(value.type, componentValue, value)) { + validComponent = true; + } + } + return validAttribute && validComponent; + } + + template <typename AT, typename VT> + bool ValidateMetricAttributeAndValue(const hidl_vec<DrmMetricGroup>& metricGroups, + const std::string& metricName, + const std::string& attributeName, const AT& attributeValue, + const std::string& componentName, const VT& componentValue) { + bool foundMetric = false; + for (const auto& group : metricGroups) { + for (const auto& metric : group.metrics) { + if (metric.name == metricName) { + foundMetric = foundMetric || ValidateMetricAttributeAndValue( + metric, attributeName, attributeValue, + componentName, componentValue); + } + } + } + return foundMetric; + } + + sp<IDrmPlugin> drmPlugin; + sp<ICryptoPlugin> cryptoPlugin; +}; + + +/** + * Helper method to open a session and verify that a non-empty + * session ID is returned + */ +SessionId DrmHalClearkeyTest::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 open as session using V1.1 API + */ +SessionId DrmHalClearkeyTest::openSession(SecurityLevel level) { + SessionId sessionId; + + auto res = drmPlugin->openSession_1_1(level, + [&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 DrmHalClearkeyTest::closeSession(const SessionId& sessionId) { + EXPECT_TRUE(drmPlugin->closeSession(sessionId).isOk()); +} + +/** + * Helper method to load keys for subsequent decrypt tests. + * These tests use predetermined key request/response to + * avoid requiring a round trip to a license server. + */ +hidl_vec<uint8_t> DrmHalClearkeyTest::loadKeys( + const SessionId& sessionId, const KeyType& type = KeyType::STREAMING) { + 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, 0x2d, 0x56, 0x39, 0x41, 0x4e, 0x48, 0x74, + 0x41, 0x4e, 0x48, 0x67, 0x22, 0x5d, 0x2c, 0x22, 0x74, 0x79, 0x70, 0x65, 0x22, 0x3a, + 0x22, 0x74, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x72, 0x79, 0x22, 0x7d}; + + 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, 0x2d, 0x56, 0x39, 0x41, 0x4e, + 0x48, 0x74, 0x41, 0x4e, 0x48, 0x67, 0x22, 0x2c, 0x22, 0x6b, 0x22, 0x3a, 0x22, 0x47, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x54, 0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x42, 0x61, 0x73, 0x65, + 0x36, 0x34, 0x67, 0x67, 0x67, 0x22, 0x7d, 0x5d, 0x7d, 0x0a}; + + hidl_string mimeType = "video/mp4"; + KeyedVector optionalParameters; + auto res = drmPlugin->getKeyRequest_1_1( + sessionId, initData, mimeType, type, 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); + + hidl_vec<uint8_t> keySetId; + res = drmPlugin->provideKeyResponse( + sessionId, knownKeyResponse, + [&](Status status, const hidl_vec<uint8_t>& myKeySetId) { + EXPECT_EQ(Status::OK, status); + EXPECT_EQ(0u, myKeySetId.size()); + keySetId = myKeySetId; + }); + EXPECT_OK(res); + return keySetId; +} + +/** + * Test openSession negative case: security level higher than supported + */ +TEST_F(DrmHalClearkeyTest, OpenSessionBadLevel) { + auto res = drmPlugin->openSession_1_1(SecurityLevel::HW_SECURE_ALL, + [&](Status status, const SessionId& /* id */) { + EXPECT_EQ(Status::ERROR_DRM_CANNOT_HANDLE, status); + }); + EXPECT_OK(res); +} + +/** + * Test getKeyRequest_1_1 via loadKeys + */ +TEST_F(DrmHalClearkeyTest, GetKeyRequest) { + auto sessionId = openSession(); + loadKeys(sessionId); + closeSession(sessionId); +} + +/** + * A get key request should fail if no sessionId is provided + */ +TEST_F(DrmHalClearkeyTest, GetKeyRequestNoSession) { + SessionId invalidSessionId; + hidl_vec<uint8_t> initData; + hidl_string mimeType = "video/mp4"; + KeyedVector optionalParameters; + auto res = drmPlugin->getKeyRequest_1_1( + 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(DrmHalClearkeyTest, GetKeyRequestOfflineKeyTypeNotSupported) { + auto sessionId = openSession(); + hidl_vec<uint8_t> initData; + hidl_string mimeType = "video/mp4"; + KeyedVector optionalParameters; + + auto res = drmPlugin->getKeyRequest_1_1( + 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 valid connected and max HDCP levels + */ +TEST_F(DrmHalClearkeyTest, GetHdcpLevels) { + auto res = drmPlugin->getHdcpLevels( + [&](Status status, const HdcpLevel &connectedLevel, + const HdcpLevel &maxLevel) { + EXPECT_EQ(Status::OK, status); + EXPECT_GE(connectedLevel, HdcpLevel::HDCP_NONE); + EXPECT_LE(maxLevel, HdcpLevel::HDCP_NO_OUTPUT); + }); + EXPECT_OK(res); +} + +/** + * Since getHdcpLevels only queries information there are no + * negative cases. + */ + +/** + * Test that the plugin returns default open and max session counts + */ +TEST_F(DrmHalClearkeyTest, GetDefaultSessionCounts) { + auto res = drmPlugin->getNumberOfSessions( + [&](Status status, uint32_t currentSessions, + uint32_t maxSessions) { + EXPECT_EQ(Status::OK, status); + EXPECT_GE(maxSessions, (uint32_t)8); + EXPECT_GE(currentSessions, (uint32_t)0); + EXPECT_LE(currentSessions, maxSessions); + }); + EXPECT_OK(res); +} + +/** + * Test that the plugin returns valid open and max session counts + * after a session is opened. + */ +TEST_F(DrmHalClearkeyTest, GetOpenSessionCounts) { + uint32_t initialSessions = 0; + auto res = drmPlugin->getNumberOfSessions( + [&](Status status, uint32_t currentSessions, + uint32_t maxSessions) { + EXPECT_EQ(Status::OK, status); + EXPECT_GE(maxSessions, (uint32_t)8); + EXPECT_GE(currentSessions, (uint32_t)0); + EXPECT_LE(currentSessions, maxSessions); + initialSessions = currentSessions; + }); + EXPECT_OK(res); + + SessionId session = openSession(); + res = drmPlugin->getNumberOfSessions( + [&](Status status, uint32_t currentSessions, + uint32_t /*maxSessions*/) { + EXPECT_EQ(Status::OK, status); + EXPECT_EQ(currentSessions, initialSessions + 1); + }); + EXPECT_OK(res); + + closeSession(session); + res = drmPlugin->getNumberOfSessions( + [&](Status status, uint32_t currentSessions, + uint32_t /*maxSessions*/) { + EXPECT_EQ(Status::OK, status); + EXPECT_EQ(currentSessions, initialSessions); + }); + EXPECT_OK(res); +} + +/** + * Since getNumberOfSessions only queries information there are no + * negative cases. + */ + +/** + * Test that the plugin returns the same security level + * by default as when it is requested explicitly + */ +TEST_F(DrmHalClearkeyTest, GetDefaultSecurityLevel) { + SessionId session = openSession(); + SecurityLevel defaultLevel; + auto res = drmPlugin->getSecurityLevel(session, + [&](Status status, SecurityLevel level) { + EXPECT_EQ(Status::OK, status); + defaultLevel = level; + }); + EXPECT_OK(res); + closeSession(session); + + session = openSession(defaultLevel); + res = drmPlugin->getSecurityLevel(session, + [&](Status status, SecurityLevel level) { + EXPECT_EQ(Status::OK, status); + EXPECT_EQ(level, defaultLevel); + }); + EXPECT_OK(res); + closeSession(session); +} + +/** + * Test that the plugin returns the lowest security level + * when it is requested + */ +TEST_F(DrmHalClearkeyTest, GetSecurityLevel) { + SessionId session = openSession(SecurityLevel::SW_SECURE_CRYPTO); + auto res = drmPlugin->getSecurityLevel(session, + [&](Status status, SecurityLevel level) { + EXPECT_EQ(Status::OK, status); + EXPECT_EQ(level, SecurityLevel::SW_SECURE_CRYPTO); + }); + EXPECT_OK(res); + closeSession(session); +} + +/** + * Test that the plugin returns the documented error + * when requesting the security level for an invalid sessionId + */ +TEST_F(DrmHalClearkeyTest, GetSecurityLevelInvalidSessionId) { + SessionId session; + auto res = drmPlugin->getSecurityLevel(session, + [&](Status status, SecurityLevel /*level*/) { + EXPECT_EQ(Status::BAD_VALUE, status); + }); + EXPECT_OK(res); +} + +/** + * Test metrics are set appropriately for open and close operations. + */ +TEST_F(DrmHalClearkeyTest, GetMetricsOpenClose) { + SessionId sessionId = openSession(); + // The first close should be successful. + closeSession(sessionId); + // The second close should fail (not opened). + EXPECT_EQ(Status::ERROR_DRM_SESSION_NOT_OPENED, drmPlugin->closeSession(sessionId)); + + auto res = drmPlugin->getMetrics([this](Status status, hidl_vec<DrmMetricGroup> metricGroups) { + EXPECT_EQ(Status::OK, status); + + // Verify the open_session metric. + EXPECT_TRUE(ValidateMetricAttributeAndValue(metricGroups, "open_session", "status", + (int64_t)0, "count", (int64_t)1)); + // Verify the close_session - success metric. + EXPECT_TRUE(ValidateMetricAttributeAndValue(metricGroups, "close_session", "status", + (int64_t)0, "count", (int64_t)1)); + // Verify the close_session - error metric. + EXPECT_TRUE(ValidateMetricAttributeAndValue(metricGroups, "close_session", "status", + (int64_t)Status::ERROR_DRM_SESSION_NOT_OPENED, + "count", (int64_t)1)); + }); + EXPECT_OK(res); +} + +/** + * Since getMetrics only queries information there are no + * negative cases. + */ + +/** + * Test that there are no secure stop ids after clearing them + */ +TEST_F(DrmHalClearkeyTest, GetSecureStopIdsCleared) { + auto stat = drmPlugin->removeAllSecureStops(); + EXPECT_OK(stat); + + bool ok = drmPlugin->getSecureStopIds( + [&](Status status, const hidl_vec<SecureStopId>& ids) { + EXPECT_EQ(Status::OK, status); + EXPECT_EQ(0u, ids.size()); + }).isOk(); + EXPECT_TRUE(ok); +} + +/** + * Test that there are secure stop ids after loading keys once + */ +TEST_F(DrmHalClearkeyTest, GetSecureStopIdsOnce) { + auto stat = drmPlugin->removeAllSecureStops(); + EXPECT_OK(stat); + + auto sessionId = openSession(); + loadKeys(sessionId); + closeSession(sessionId); + + auto res = drmPlugin->getSecureStopIds( + [&](Status status, const hidl_vec<SecureStopId>& ids) { + EXPECT_EQ(Status::OK, status); + EXPECT_EQ(1u, ids.size()); + }); + EXPECT_OK(res); + + stat = drmPlugin->removeAllSecureStops(); + EXPECT_OK(stat); + + res = drmPlugin->getSecureStopIds( + [&](Status status, const hidl_vec<SecureStopId>& ids) { + EXPECT_EQ(Status::OK, status); + EXPECT_EQ(0u, ids.size()); + }); + EXPECT_OK(res); +} + +/** + * Since getSecureStopIds only queries information there are no + * negative cases. + */ + +/** + * Test that the clearkey plugin reports no secure stops when + * there are none. + */ +TEST_F(DrmHalClearkeyTest, GetNoSecureStops) { + auto stat = drmPlugin->removeAllSecureStops(); + EXPECT_OK(stat); + + auto res = drmPlugin->getSecureStops( + [&](Status status, const hidl_vec<SecureStop>& stops) { + EXPECT_EQ(Status::OK, status); + EXPECT_EQ(0u, stops.size()); + }); + EXPECT_OK(res); +} + +/** + * Test get/remove of one secure stop + */ +TEST_F(DrmHalClearkeyTest, GetOneSecureStopAndRemoveIt) { + auto stat = drmPlugin->removeAllSecureStops(); + EXPECT_OK(stat); + + auto sessionId = openSession(); + loadKeys(sessionId); + closeSession(sessionId); + + auto res = drmPlugin->getSecureStops( + [&](Status status, const hidl_vec<SecureStop>& stops) { + EXPECT_EQ(Status::OK, status); + EXPECT_EQ(1u, stops.size()); + }); + EXPECT_OK(res); + + stat = drmPlugin->removeAllSecureStops(); + EXPECT_OK(stat); + + res = drmPlugin->getSecureStops( + [&](Status status, const hidl_vec<SecureStop>& stops) { + EXPECT_EQ(Status::OK, status); + EXPECT_EQ(0u, stops.size()); + }); + EXPECT_OK(res); +} + +/** + * Since getSecureStops only queries information there are no + * negative cases. + */ + +/** + * Test that there are no secure stops after clearing them + */ +TEST_F(DrmHalClearkeyTest, GetSecureStopsCleared) { + auto stat = drmPlugin->removeAllSecureStops(); + EXPECT_OK(stat); + + auto res = drmPlugin->getSecureStops( + [&](Status status, const hidl_vec<SecureStop>& stops) { + EXPECT_EQ(Status::OK, status); + EXPECT_EQ(0u, stops.size()); + }); + EXPECT_OK(res); +} + +/** + * Test that there are secure stops after loading keys once + */ +TEST_F(DrmHalClearkeyTest, GetSecureStopsOnce) { + auto stat = drmPlugin->removeAllSecureStops(); + EXPECT_OK(stat); + + auto sessionId = openSession(); + loadKeys(sessionId); + closeSession(sessionId); + + auto res = drmPlugin->getSecureStops( + [&](Status status, const hidl_vec<SecureStop>& stops) { + EXPECT_EQ(Status::OK, status); + EXPECT_EQ(1u, stops.size()); + }); + EXPECT_OK(res); + + stat = drmPlugin->removeAllSecureStops(); + EXPECT_OK(stat); + + res = drmPlugin->getSecureStops( + [&](Status status, const hidl_vec<SecureStop>& stops) { + EXPECT_EQ(Status::OK, status); + EXPECT_EQ(0u, stops.size()); + }); + EXPECT_OK(res); +} + +/** + * Since getSecureStops only queries information there are no + * negative cases. + */ + +/** + * Test that releasing a secure stop with empty + * release message fails with the documented error + */ +TEST_F(DrmHalClearkeyTest, ReleaseEmptySecureStop) { + SecureStopRelease emptyRelease = {.opaqueData = hidl_vec<uint8_t>()}; + Status status = drmPlugin->releaseSecureStops(emptyRelease); + EXPECT_EQ(Status::BAD_VALUE, status); +} + +/** + * Helper function to create a secure release message for + * a secure stop. The clearkey secure stop release format + * is just a count followed by the secure stop opaque data. + */ +SecureStopRelease makeSecureRelease(const SecureStop &stop) { + std::vector<uint8_t> stopData = stop.opaqueData; + std::vector<uint8_t> buffer; + std::string count = "0001"; + + auto it = buffer.insert(buffer.begin(), count.begin(), count.end()); + buffer.insert(it + count.size(), stopData.begin(), stopData.end()); + SecureStopRelease release = { .opaqueData = hidl_vec<uint8_t>(buffer) }; + return release; +} + +/** + * Test that releasing one secure stop works + */ +TEST_F(DrmHalClearkeyTest, ReleaseOneSecureStop) { + + auto stat = drmPlugin->removeAllSecureStops(); + EXPECT_OK(stat); + + auto sessionId = openSession(); + loadKeys(sessionId); + closeSession(sessionId); + + SecureStopRelease release; + auto res = drmPlugin->getSecureStops( + [&](Status status, const hidl_vec<SecureStop>& stops) { + EXPECT_EQ(Status::OK, status); + EXPECT_EQ(1u, stops.size()); + release = makeSecureRelease(stops[0]); + }); + EXPECT_OK(res); + + stat = drmPlugin->releaseSecureStops(release); + EXPECT_OK(stat); + + res = drmPlugin->getSecureStops( + [&](Status status, const hidl_vec<SecureStop>& stops) { + EXPECT_EQ(Status::OK, status); + EXPECT_EQ(0u, stops.size()); + }); + EXPECT_OK(res); +} + + +/** + * Test that removing a secure stop with an empty ID returns + * documented error + */ +TEST_F(DrmHalClearkeyTest, RemoveEmptySecureStopId) { + hidl_vec<uint8_t> emptyId; + auto stat = drmPlugin->removeSecureStop(emptyId); + EXPECT_OK(stat); + EXPECT_EQ(Status::BAD_VALUE, stat); +} + +/** + * Test that removing a secure stop after it has already + * been removed fails with the documented error code. + */ +TEST_F(DrmHalClearkeyTest, RemoveRemovedSecureStopId) { + auto stat = drmPlugin->removeAllSecureStops(); + EXPECT_OK(stat); + + auto sessionId = openSession(); + loadKeys(sessionId); + closeSession(sessionId); + SecureStopId ssid; + + auto res = drmPlugin->getSecureStopIds( + [&](Status status, const hidl_vec<SecureStopId>& ids) { + EXPECT_EQ(Status::OK, status); + EXPECT_EQ(1u, ids.size()); + ssid = ids[0]; + }); + EXPECT_OK(res); + + stat = drmPlugin->removeAllSecureStops(); + EXPECT_OK(stat); + + Status status = drmPlugin->removeSecureStop(ssid); + EXPECT_EQ(Status::BAD_VALUE, status); +} + +/** + * Test that removing a secure stop by id works + */ +TEST_F(DrmHalClearkeyTest, RemoveSecureStopById) { + auto stat = drmPlugin->removeAllSecureStops(); + EXPECT_OK(stat); + + auto sessionId = openSession(); + loadKeys(sessionId); + closeSession(sessionId); + SecureStopId ssid; + + auto res = drmPlugin->getSecureStopIds( + [&](Status status, const hidl_vec<SecureStopId>& ids) { + EXPECT_EQ(Status::OK, status); + EXPECT_EQ(1u, ids.size()); + ssid = ids[0]; + }); + EXPECT_OK(res); + + stat = drmPlugin->removeSecureStop(ssid); + EXPECT_OK(stat); + + res = drmPlugin->getSecureStopIds( + [&](Status status, const hidl_vec<SecureStopId>& ids) { + EXPECT_EQ(Status::OK, status); + EXPECT_EQ(0u, ids.size()); + }); + EXPECT_OK(res); +} + + +int main(int argc, char** argv) { + ::testing::AddGlobalTestEnvironment(DrmHidlEnvironment::Instance()); + ::testing::InitGoogleTest(&argc, argv); + DrmHidlEnvironment::Instance()->init(&argc, argv); + int status = RUN_ALL_TESTS(); + ALOGI("Test result = %d", status); + return status; +} diff --git a/drm/Android.bp b/drm/Android.bp deleted file mode 100644 index ed19a3703..000000000 --- a/drm/Android.bp +++ /dev/null @@ -1,6 +0,0 @@ -// This is an autogenerated file, do not edit. -subdirs = [ - "1.0", - "1.0/default", - "1.0/vts/functional", -] |