diff options
63 files changed, 3081 insertions, 12 deletions
diff --git a/drm/mediadrm/plugins/clearkey/common/Android.bp b/drm/mediadrm/plugins/clearkey/common/Android.bp new file mode 100644 index 0000000000..2c674e1f29 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/common/Android.bp @@ -0,0 +1,38 @@ +// +// Copyright (C) 2018 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +cc_library_static { + name: "libclearkeycommon", + vendor: true, + + srcs: [ + "ClearKeyUUID.cpp", + "Utils.cpp", + ], + + cflags: ["-Wall", "-Werror"], + + include_dirs: ["frameworks/av/include"], + + shared_libs: ["libutils"], + + export_include_dirs: ["include"], + + sanitize: { + integer_overflow: true, + }, +} + diff --git a/drm/mediadrm/plugins/clearkey/ClearKeyUUID.cpp b/drm/mediadrm/plugins/clearkey/common/ClearKeyUUID.cpp index 0259a42a15..0259a42a15 100644 --- a/drm/mediadrm/plugins/clearkey/ClearKeyUUID.cpp +++ b/drm/mediadrm/plugins/clearkey/common/ClearKeyUUID.cpp diff --git a/drm/mediadrm/plugins/clearkey/Utils.cpp b/drm/mediadrm/plugins/clearkey/common/Utils.cpp index 93c643b636..93c643b636 100644 --- a/drm/mediadrm/plugins/clearkey/Utils.cpp +++ b/drm/mediadrm/plugins/clearkey/common/Utils.cpp diff --git a/drm/mediadrm/plugins/clearkey/ClearKeyUUID.h b/drm/mediadrm/plugins/clearkey/common/include/ClearKeyUUID.h index ac99418619..ac99418619 100644 --- a/drm/mediadrm/plugins/clearkey/ClearKeyUUID.h +++ b/drm/mediadrm/plugins/clearkey/common/include/ClearKeyUUID.h diff --git a/drm/mediadrm/plugins/clearkey/MimeType.h b/drm/mediadrm/plugins/clearkey/common/include/MimeType.h index 085f17a3e8..085f17a3e8 100644 --- a/drm/mediadrm/plugins/clearkey/MimeType.h +++ b/drm/mediadrm/plugins/clearkey/common/include/MimeType.h diff --git a/drm/mediadrm/plugins/clearkey/Utils.h b/drm/mediadrm/plugins/clearkey/common/include/Utils.h index 25431247e0..25431247e0 100644 --- a/drm/mediadrm/plugins/clearkey/Utils.h +++ b/drm/mediadrm/plugins/clearkey/common/include/Utils.h diff --git a/drm/mediadrm/plugins/clearkey/AesCtrDecryptor.cpp b/drm/mediadrm/plugins/clearkey/default/AesCtrDecryptor.cpp index f7106b275e..f7106b275e 100644 --- a/drm/mediadrm/plugins/clearkey/AesCtrDecryptor.cpp +++ b/drm/mediadrm/plugins/clearkey/default/AesCtrDecryptor.cpp diff --git a/drm/mediadrm/plugins/clearkey/Android.bp b/drm/mediadrm/plugins/clearkey/default/Android.bp index 4b7a63c9a2..7ba57088cc 100644 --- a/drm/mediadrm/plugins/clearkey/Android.bp +++ b/drm/mediadrm/plugins/clearkey/default/Android.bp @@ -16,10 +16,10 @@ cc_library_shared { name: "libdrmclearkeyplugin", + vendor: true, srcs: [ "AesCtrDecryptor.cpp", - "ClearKeyUUID.cpp", "CreatePluginFactories.cpp", "CryptoFactory.cpp", "CryptoPlugin.cpp", @@ -29,10 +29,8 @@ cc_library_shared { "JsonWebKey.cpp", "Session.cpp", "SessionLibrary.cpp", - "Utils.cpp", ], - vendor: true, relative_install_path: "mediadrm", cflags: ["-Wall", "-Werror"], @@ -44,16 +42,20 @@ cc_library_shared { "libutils", ], - static_libs: ["libjsmn"], + static_libs: [ + "libclearkeycommon", + "libjsmn" + ], + + local_include_dirs: ["include"], + export_include_dirs: ["include"], + export_static_lib_headers: ["libjsmn"], include_dirs: [ "frameworks/native/include", "frameworks/av/include", ], - export_include_dirs: ["."], - export_static_lib_headers: ["libjsmn"], - sanitize: { integer_overflow: true, }, diff --git a/drm/mediadrm/plugins/clearkey/CreatePluginFactories.cpp b/drm/mediadrm/plugins/clearkey/default/CreatePluginFactories.cpp index ec1420e723..ec1420e723 100644 --- a/drm/mediadrm/plugins/clearkey/CreatePluginFactories.cpp +++ b/drm/mediadrm/plugins/clearkey/default/CreatePluginFactories.cpp diff --git a/drm/mediadrm/plugins/clearkey/CryptoFactory.cpp b/drm/mediadrm/plugins/clearkey/default/CryptoFactory.cpp index eeb64c3d34..f15f92b12e 100644 --- a/drm/mediadrm/plugins/clearkey/CryptoFactory.cpp +++ b/drm/mediadrm/plugins/clearkey/default/CryptoFactory.cpp @@ -15,7 +15,7 @@ */ //#define LOG_NDEBUG 0 -#define LOG_TAG "ClearKeyCryptoPlugin" +#define LOG_TAG "ClearKeyCryptoFactory" #include <utils/Log.h> #include <utils/Errors.h> diff --git a/drm/mediadrm/plugins/clearkey/CryptoPlugin.cpp b/drm/mediadrm/plugins/clearkey/default/CryptoPlugin.cpp index ee97976387..ee97976387 100644 --- a/drm/mediadrm/plugins/clearkey/CryptoPlugin.cpp +++ b/drm/mediadrm/plugins/clearkey/default/CryptoPlugin.cpp diff --git a/drm/mediadrm/plugins/clearkey/DrmFactory.cpp b/drm/mediadrm/plugins/clearkey/default/DrmFactory.cpp index c83321b2e6..8301e40757 100644 --- a/drm/mediadrm/plugins/clearkey/DrmFactory.cpp +++ b/drm/mediadrm/plugins/clearkey/default/DrmFactory.cpp @@ -15,7 +15,7 @@ */ //#define LOG_NDEBUG 0 -#define LOG_TAG "ClearKeyCryptoPlugin" +#define LOG_TAG "ClearKeyDrmFactory" #include <utils/Log.h> #include <utils/Errors.h> diff --git a/drm/mediadrm/plugins/clearkey/DrmPlugin.cpp b/drm/mediadrm/plugins/clearkey/default/DrmPlugin.cpp index 944002d0fa..1b8b8c1e65 100644 --- a/drm/mediadrm/plugins/clearkey/DrmPlugin.cpp +++ b/drm/mediadrm/plugins/clearkey/default/DrmPlugin.cpp @@ -15,7 +15,7 @@ */ //#define LOG_NDEBUG 0 -#define LOG_TAG "ClearKeyCryptoPlugin" +#define LOG_TAG "ClearKeyDrmPlugin" #include <utils/Log.h> #include <media/stagefright/MediaErrors.h> diff --git a/drm/mediadrm/plugins/clearkey/InitDataParser.cpp b/drm/mediadrm/plugins/clearkey/default/InitDataParser.cpp index caff3939be..caff3939be 100644 --- a/drm/mediadrm/plugins/clearkey/InitDataParser.cpp +++ b/drm/mediadrm/plugins/clearkey/default/InitDataParser.cpp diff --git a/drm/mediadrm/plugins/clearkey/JsonWebKey.cpp b/drm/mediadrm/plugins/clearkey/default/JsonWebKey.cpp index 53ffae4ccd..53ffae4ccd 100644 --- a/drm/mediadrm/plugins/clearkey/JsonWebKey.cpp +++ b/drm/mediadrm/plugins/clearkey/default/JsonWebKey.cpp diff --git a/drm/mediadrm/plugins/clearkey/Session.cpp b/drm/mediadrm/plugins/clearkey/default/Session.cpp index d210f5e610..b3ceaeca51 100644 --- a/drm/mediadrm/plugins/clearkey/Session.cpp +++ b/drm/mediadrm/plugins/clearkey/default/Session.cpp @@ -15,7 +15,7 @@ */ //#define LOG_NDEBUG 0 -#define LOG_TAG "ClearKeyCryptoPlugin" +#define LOG_TAG "ClearKeySession" #include <utils/Log.h> #include <media/stagefright/MediaErrors.h> diff --git a/drm/mediadrm/plugins/clearkey/SessionLibrary.cpp b/drm/mediadrm/plugins/clearkey/default/SessionLibrary.cpp index 0419f9776a..529230ea74 100644 --- a/drm/mediadrm/plugins/clearkey/SessionLibrary.cpp +++ b/drm/mediadrm/plugins/clearkey/default/SessionLibrary.cpp @@ -15,7 +15,7 @@ */ //#define LOG_NDEBUG 0 -#define LOG_TAG "ClearKeyCryptoPlugin" +#define LOG_TAG "ClearKeySessionLibrary" #include <utils/Log.h> #include <utils/String8.h> diff --git a/drm/mediadrm/plugins/clearkey/AesCtrDecryptor.h b/drm/mediadrm/plugins/clearkey/default/include/AesCtrDecryptor.h index edb84458a7..edb84458a7 100644 --- a/drm/mediadrm/plugins/clearkey/AesCtrDecryptor.h +++ b/drm/mediadrm/plugins/clearkey/default/include/AesCtrDecryptor.h diff --git a/drm/mediadrm/plugins/clearkey/ClearKeyDrmProperties.h b/drm/mediadrm/plugins/clearkey/default/include/ClearKeyDrmProperties.h index a99e17447e..a99e17447e 100644 --- a/drm/mediadrm/plugins/clearkey/ClearKeyDrmProperties.h +++ b/drm/mediadrm/plugins/clearkey/default/include/ClearKeyDrmProperties.h diff --git a/drm/mediadrm/plugins/clearkey/ClearKeyTypes.h b/drm/mediadrm/plugins/clearkey/default/include/ClearKeyTypes.h index a28959ae5a..a28959ae5a 100644 --- a/drm/mediadrm/plugins/clearkey/ClearKeyTypes.h +++ b/drm/mediadrm/plugins/clearkey/default/include/ClearKeyTypes.h diff --git a/drm/mediadrm/plugins/clearkey/CreatePluginFactories.h b/drm/mediadrm/plugins/clearkey/default/include/CreatePluginFactories.h index d9acec1ad6..d9acec1ad6 100644 --- a/drm/mediadrm/plugins/clearkey/CreatePluginFactories.h +++ b/drm/mediadrm/plugins/clearkey/default/include/CreatePluginFactories.h diff --git a/drm/mediadrm/plugins/clearkey/CryptoFactory.h b/drm/mediadrm/plugins/clearkey/default/include/CryptoFactory.h index 568bc4b0e4..568bc4b0e4 100644 --- a/drm/mediadrm/plugins/clearkey/CryptoFactory.h +++ b/drm/mediadrm/plugins/clearkey/default/include/CryptoFactory.h diff --git a/drm/mediadrm/plugins/clearkey/CryptoPlugin.h b/drm/mediadrm/plugins/clearkey/default/include/CryptoPlugin.h index a6d2f7be71..a6d2f7be71 100644 --- a/drm/mediadrm/plugins/clearkey/CryptoPlugin.h +++ b/drm/mediadrm/plugins/clearkey/default/include/CryptoPlugin.h diff --git a/drm/mediadrm/plugins/clearkey/DrmFactory.h b/drm/mediadrm/plugins/clearkey/default/include/DrmFactory.h index 0bc0843929..0bc0843929 100644 --- a/drm/mediadrm/plugins/clearkey/DrmFactory.h +++ b/drm/mediadrm/plugins/clearkey/default/include/DrmFactory.h diff --git a/drm/mediadrm/plugins/clearkey/DrmPlugin.h b/drm/mediadrm/plugins/clearkey/default/include/DrmPlugin.h index 4fa42e50af..4fa42e50af 100644 --- a/drm/mediadrm/plugins/clearkey/DrmPlugin.h +++ b/drm/mediadrm/plugins/clearkey/default/include/DrmPlugin.h diff --git a/drm/mediadrm/plugins/clearkey/InitDataParser.h b/drm/mediadrm/plugins/clearkey/default/include/InitDataParser.h index a9707bfdbc..a9707bfdbc 100644 --- a/drm/mediadrm/plugins/clearkey/InitDataParser.h +++ b/drm/mediadrm/plugins/clearkey/default/include/InitDataParser.h diff --git a/drm/mediadrm/plugins/clearkey/JsonWebKey.h b/drm/mediadrm/plugins/clearkey/default/include/JsonWebKey.h index 6ae50eecfe..6ae50eecfe 100644 --- a/drm/mediadrm/plugins/clearkey/JsonWebKey.h +++ b/drm/mediadrm/plugins/clearkey/default/include/JsonWebKey.h diff --git a/drm/mediadrm/plugins/clearkey/Session.h b/drm/mediadrm/plugins/clearkey/default/include/Session.h index 09335069df..09335069df 100644 --- a/drm/mediadrm/plugins/clearkey/Session.h +++ b/drm/mediadrm/plugins/clearkey/default/include/Session.h diff --git a/drm/mediadrm/plugins/clearkey/SessionLibrary.h b/drm/mediadrm/plugins/clearkey/default/include/SessionLibrary.h index 6236fff47c..6236fff47c 100644 --- a/drm/mediadrm/plugins/clearkey/SessionLibrary.h +++ b/drm/mediadrm/plugins/clearkey/default/include/SessionLibrary.h diff --git a/drm/mediadrm/plugins/clearkey/tests/AesCtrDecryptorUnittest.cpp b/drm/mediadrm/plugins/clearkey/default/tests/AesCtrDecryptorUnittest.cpp index 5db8290a91..5db8290a91 100644 --- a/drm/mediadrm/plugins/clearkey/tests/AesCtrDecryptorUnittest.cpp +++ b/drm/mediadrm/plugins/clearkey/default/tests/AesCtrDecryptorUnittest.cpp diff --git a/drm/mediadrm/plugins/clearkey/tests/Android.bp b/drm/mediadrm/plugins/clearkey/default/tests/Android.bp index ea17bbbd98..4419865705 100644 --- a/drm/mediadrm/plugins/clearkey/tests/Android.bp +++ b/drm/mediadrm/plugins/clearkey/default/tests/Android.bp @@ -29,6 +29,8 @@ cc_test { "JsonWebKeyUnittest.cpp", ], + static_libs: ["libclearkeycommon"], + shared_libs: [ "libcrypto", "libdrmclearkeyplugin", diff --git a/drm/mediadrm/plugins/clearkey/tests/InitDataParserUnittest.cpp b/drm/mediadrm/plugins/clearkey/default/tests/InitDataParserUnittest.cpp index 8c496566cc..8c496566cc 100644 --- a/drm/mediadrm/plugins/clearkey/tests/InitDataParserUnittest.cpp +++ b/drm/mediadrm/plugins/clearkey/default/tests/InitDataParserUnittest.cpp diff --git a/drm/mediadrm/plugins/clearkey/tests/JsonWebKeyUnittest.cpp b/drm/mediadrm/plugins/clearkey/default/tests/JsonWebKeyUnittest.cpp index d9f3ea6703..d9f3ea6703 100644 --- a/drm/mediadrm/plugins/clearkey/tests/JsonWebKeyUnittest.cpp +++ b/drm/mediadrm/plugins/clearkey/default/tests/JsonWebKeyUnittest.cpp diff --git a/drm/mediadrm/plugins/clearkey/hidl/AesCtrDecryptor.cpp b/drm/mediadrm/plugins/clearkey/hidl/AesCtrDecryptor.cpp new file mode 100644 index 0000000000..2fce07902e --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/AesCtrDecryptor.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "hidl_ClearkeyDecryptor" +#include <utils/Log.h> + +#include <openssl/aes.h> + +#include "AesCtrDecryptor.h" +#include "ClearKeyTypes.h" + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +using ::android::hardware::drm::V1_0::SubSample; +using ::android::hardware::drm::V1_0::Status; + +static const size_t kBlockBitCount = kBlockSize * 8; + +Status AesCtrDecryptor::decrypt( + const std::vector<uint8_t>& key, + const Iv iv, const uint8_t* source, + uint8_t* destination, + const std::vector<SubSample> subSamples, + size_t numSubSamples, + size_t* bytesDecryptedOut) { + uint32_t blockOffset = 0; + uint8_t previousEncryptedCounter[kBlockSize]; + memset(previousEncryptedCounter, 0, kBlockSize); + + if (key.size() != kBlockSize || (sizeof(Iv) / sizeof(uint8_t)) != kBlockSize) { + android_errorWriteLog(0x534e4554, "63982768"); + return Status::ERROR_DRM_DECRYPT; + } + + size_t offset = 0; + AES_KEY opensslKey; + AES_set_encrypt_key(key.data(), kBlockBitCount, &opensslKey); + Iv opensslIv; + memcpy(opensslIv, iv, sizeof(opensslIv)); + + for (size_t i = 0; i < numSubSamples; ++i) { + const SubSample& subSample = subSamples[i]; + + if (subSample.numBytesOfClearData > 0) { + memcpy(destination + offset, source + offset, + subSample.numBytesOfClearData); + offset += subSample.numBytesOfClearData; + } + + if (subSample.numBytesOfEncryptedData > 0) { + AES_ctr128_encrypt(source + offset, destination + offset, + subSample.numBytesOfEncryptedData, &opensslKey, + opensslIv, previousEncryptedCounter, + &blockOffset); + offset += subSample.numBytesOfEncryptedData; + } + } + + *bytesDecryptedOut = offset; + return Status::OK; +} + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android + diff --git a/drm/mediadrm/plugins/clearkey/hidl/Android.bp b/drm/mediadrm/plugins/clearkey/hidl/Android.bp new file mode 100644 index 0000000000..341d4f645d --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/Android.bp @@ -0,0 +1,68 @@ +// +// Copyright (C) 2018 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +cc_binary { + name: "android.hardware.drm@1.1-service.clearkey", + vendor: true, + + srcs: [ + "AesCtrDecryptor.cpp", + "Base64.cpp", + "Buffer.cpp", + "CreatePluginFactories.cpp", + "CryptoFactory.cpp", + "CryptoPlugin.cpp", + "DrmFactory.cpp", + "DrmPlugin.cpp", + "InitDataParser.cpp", + "JsonWebKey.cpp", + "Session.cpp", + "SessionLibrary.cpp", + "service.cpp", + ], + + relative_install_path: "hw", + + cflags: ["-Wall", "-Werror"], + init_rc: ["android.hardware.drm@1.1-service.clearkey.rc"], + + shared_libs: [ + "android.hardware.drm@1.0", + "android.hardware.drm@1.1", + "libbase", + "libbinder", + "libcrypto", + "libhidlbase", + "libhidlmemory", + "libhidltransport", + "liblog", + "libutils", + ], + + static_libs: [ + "libclearkeycommon", + "libjsmn", + ], + + local_include_dirs: ["include"], + + export_static_lib_headers: ["libjsmn"], + + sanitize: { + integer_overflow: true, + }, +} + diff --git a/drm/mediadrm/plugins/clearkey/hidl/Base64.cpp b/drm/mediadrm/plugins/clearkey/hidl/Base64.cpp new file mode 100644 index 0000000000..c2ed7513f4 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/Base64.cpp @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Base64.h" + +#include <string> + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +sp<Buffer> decodeBase64(const std::string &s) { + size_t n = s.size(); + + if ((n % 4) != 0) { + return nullptr; + } + + size_t padding = 0; + if (n >= 1 && s.c_str()[n - 1] == '=') { + padding = 1; + + if (n >= 2 && s.c_str()[n - 2] == '=') { + padding = 2; + + if (n >= 3 && s.c_str()[n - 3] == '=') { + padding = 3; + } + } + } + + // We divide first to avoid overflow. It's OK to do this because we + // already made sure that n % 4 == 0. + size_t outLen = (n / 4) * 3 - padding; + + sp<Buffer> buffer = new Buffer(outLen); + uint8_t *out = buffer->data(); + if (out == nullptr || buffer->size() < outLen) { + return nullptr; + } + + size_t j = 0; + uint32_t accum = 0; + for (size_t i = 0; i < n; ++i) { + char c = s.c_str()[i]; + unsigned value; + if (c >= 'A' && c <= 'Z') { + value = c - 'A'; + } else if (c >= 'a' && c <= 'z') { + value = 26 + c - 'a'; + } else if (c >= '0' && c <= '9') { + value = 52 + c - '0'; + } else if (c == '+' || c == '-') { + value = 62; + } else if (c == '/' || c == '_') { + value = 63; + } else if (c != '=') { + return nullptr; + } else { + if (i < n - padding) { + return nullptr; + } + + value = 0; + } + + accum = (accum << 6) | value; + + if (((i + 1) % 4) == 0) { + if (j < outLen) { out[j++] = (accum >> 16); } + if (j < outLen) { out[j++] = (accum >> 8) & 0xff; } + if (j < outLen) { out[j++] = accum & 0xff; } + + accum = 0; + } + } + + return buffer; +} + +static char encode6Bit(unsigned x) { + if (x <= 25) { + return 'A' + x; + } else if (x <= 51) { + return 'a' + x - 26; + } else if (x <= 61) { + return '0' + x - 52; + } else if (x == 62) { + return '+'; + } else { + return '/'; + } +} + +void encodeBase64(const void *_data, size_t size, std::string *out) { + out->clear(); + + const uint8_t *data = (const uint8_t *)_data; + + size_t i; + for (i = 0; i < (size / 3) * 3; i += 3) { + uint8_t x1 = data[i]; + uint8_t x2 = data[i + 1]; + uint8_t x3 = data[i + 2]; + + out->push_back(encode6Bit(x1 >> 2)); + out->push_back(encode6Bit((x1 << 4 | x2 >> 4) & 0x3f)); + out->push_back(encode6Bit((x2 << 2 | x3 >> 6) & 0x3f)); + out->push_back(encode6Bit(x3 & 0x3f)); + } + switch (size % 3) { + case 0: + break; + case 2: + { + uint8_t x1 = data[i]; + uint8_t x2 = data[i + 1]; + out->push_back(encode6Bit(x1 >> 2)); + out->push_back(encode6Bit((x1 << 4 | x2 >> 4) & 0x3f)); + out->push_back(encode6Bit((x2 << 2) & 0x3f)); + out->push_back('='); + break; + } + default: + { + uint8_t x1 = data[i]; + out->push_back(encode6Bit(x1 >> 2)); + out->push_back(encode6Bit((x1 << 4) & 0x3f)); + out->append("=="); + break; + } + } +} + +void encodeBase64Url(const void *_data, size_t size, std::string *out) { + encodeBase64(_data, size, out); + + if ((std::string::npos != out->find("+")) || + (std::string::npos != out->find("/"))) { + size_t outLen = out->size(); + char *base64url = new char[outLen]; + for (size_t i = 0; i < outLen; ++i) { + if (out->c_str()[i] == '+') + base64url[i] = '-'; + else if (out->c_str()[i] == '/') + base64url[i] = '_'; + else + base64url[i] = out->c_str()[i]; + } + + out->assign(base64url, outLen); + delete[] base64url; + } +} + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/Buffer.cpp b/drm/mediadrm/plugins/clearkey/hidl/Buffer.cpp new file mode 100644 index 0000000000..e58f58a379 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/Buffer.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Buffer.h" + +#include <android/hardware/drm/1.0/types.h> + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +Buffer::Buffer(size_t capacity) + : mRangeOffset(0), + mOwnsData(true) { + mData = malloc(capacity); + if (mData == nullptr) { + mCapacity = 0; + mRangeLength = 0; + } else { + mCapacity = capacity; + mRangeLength = capacity; + } +} + +Buffer::~Buffer() { + if (mOwnsData) { + if (mData != nullptr) { + free(mData); + mData = nullptr; + } + } +} + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/CreatePluginFactories.cpp b/drm/mediadrm/plugins/clearkey/hidl/CreatePluginFactories.cpp new file mode 100644 index 0000000000..1ba5c6a9ec --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/CreatePluginFactories.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CreatePluginFactories.h" + +#include "CryptoFactory.h" +#include "DrmFactory.h" + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +extern "C" { + +IDrmFactory* createDrmFactory() { + return new DrmFactory(); +} + +ICryptoFactory* createCryptoFactory() { + return new CryptoFactory(); +} + +} // extern "C" + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/CryptoFactory.cpp b/drm/mediadrm/plugins/clearkey/hidl/CryptoFactory.cpp new file mode 100644 index 0000000000..0848cef1f2 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/CryptoFactory.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "hidl_ClearKeyCryptoFactory" +#include <utils/Log.h> + +#include "CryptoFactory.h" + +#include "ClearKeyUUID.h" +#include "CryptoPlugin.h" +#include "TypeConvert.h" + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +Return<bool> CryptoFactory::isCryptoSchemeSupported( + const hidl_array<uint8_t, 16> &uuid) +{ + return clearkeydrm::isClearKeyUUID(uuid.data()); +} + +Return<void> CryptoFactory::createPlugin( + const hidl_array<uint8_t, 16> &uuid, + const hidl_vec<uint8_t> &initData, + createPlugin_cb _hidl_cb) { + + if (!isCryptoSchemeSupported(uuid.data())) { + ALOGE("Clearkey Drm HAL: failed to create clearkey plugin, " \ + "invalid crypto scheme"); + _hidl_cb(Status::BAD_VALUE, nullptr); + return Void(); + } + + CryptoPlugin *cryptoPlugin = new CryptoPlugin(initData); + Status status = cryptoPlugin->getInitStatus(); + if (status == Status::OK) { + _hidl_cb(Status::OK, cryptoPlugin); + } else { + delete cryptoPlugin; + _hidl_cb(status, nullptr); + } + return Void(); +} + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android + diff --git a/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp b/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp new file mode 100644 index 0000000000..f33f94e711 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "hidl_ClearKeyCryptoPlugin" +#include <utils/Log.h> + +#include "CryptoPlugin.h" +#include "SessionLibrary.h" +#include "TypeConvert.h" + +#include <hidlmemory/mapping.h> + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +using ::android::hardware::drm::V1_0::BufferType; + +Return<void> CryptoPlugin::setSharedBufferBase( + const hidl_memory& base, uint32_t bufferId) { + sp<IMemory> hidlMemory = mapMemory(base); + ALOGE_IF(hidlMemory == nullptr, "mapMemory returns nullptr"); + + // allow mapMemory to return nullptr + mSharedBufferMap[bufferId] = hidlMemory; + return Void(); +} + +// Returns negative values for error code and positive values for the size of +// decrypted data. In theory, the output size can be larger than the input +// size, but in practice this will never happen for AES-CTR. +Return<void> CryptoPlugin::decrypt( + bool secure, + const hidl_array<uint8_t, KEY_ID_SIZE>& keyId, + const hidl_array<uint8_t, KEY_IV_SIZE>& iv, + Mode mode, + const Pattern& pattern, + const hidl_vec<SubSample>& subSamples, + const SharedBuffer& source, + uint64_t offset, + const DestinationBuffer& destination, + decrypt_cb _hidl_cb) { + UNUSED(pattern); + + if (secure) { + _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, 0, + "Secure decryption is not supported with ClearKey."); + return Void(); + } + + if (mSharedBufferMap.find(source.bufferId) == mSharedBufferMap.end()) { + _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, 0, + "source decrypt buffer base not set"); + return Void(); + } + + if (destination.type == BufferType::SHARED_MEMORY) { + const SharedBuffer& dest = destination.nonsecureMemory; + if (mSharedBufferMap.find(dest.bufferId) == mSharedBufferMap.end()) { + _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, 0, + "destination decrypt buffer base not set"); + return Void(); + } + } + + sp<IMemory> sourceBase = mSharedBufferMap[source.bufferId]; + if (sourceBase == nullptr) { + _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, 0, "source is a nullptr"); + return Void(); + } + + if (source.offset + offset + source.size > sourceBase->getSize()) { + _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, 0, "invalid buffer size"); + return Void(); + } + + uint8_t *base = static_cast<uint8_t *> + (static_cast<void *>(sourceBase->getPointer())); + uint8_t* srcPtr = static_cast<uint8_t *>(base + source.offset + offset); + void* destPtr = NULL; + if (destination.type == BufferType::SHARED_MEMORY) { + const SharedBuffer& destBuffer = destination.nonsecureMemory; + sp<IMemory> destBase = mSharedBufferMap[destBuffer.bufferId]; + if (destBase == nullptr) { + _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, 0, "destination is a nullptr"); + return Void(); + } + + if (destBuffer.offset + destBuffer.size > destBase->getSize()) { + _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, 0, "invalid buffer size"); + return Void(); + } + destPtr = static_cast<void *>(base + destination.nonsecureMemory.offset); + } else if (destination.type == BufferType::NATIVE_HANDLE) { + native_handle_t *handle = const_cast<native_handle_t *>( + destination.secureMemory.getNativeHandle()); + destPtr = static_cast<void *>(handle); + } + + // Calculate the output buffer size and determine if any subsamples are + // encrypted. + size_t destSize = 0; + bool haveEncryptedSubsamples = false; + for (size_t i = 0; i < subSamples.size(); i++) { + const SubSample &subSample = subSamples[i]; + destSize += subSample.numBytesOfClearData; + destSize += subSample.numBytesOfEncryptedData; + if (subSample.numBytesOfEncryptedData > 0) { + haveEncryptedSubsamples = true; + } + } + + if (mode == Mode::UNENCRYPTED) { + if (haveEncryptedSubsamples) { + _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, 0, + "Encrypted subsamples found in allegedly unencrypted data."); + return Void(); + } + + size_t offset = 0; + for (size_t i = 0; i < subSamples.size(); ++i) { + const SubSample& subSample = subSamples[i]; + if (subSample.numBytesOfClearData != 0) { + memcpy(reinterpret_cast<uint8_t*>(destPtr) + offset, + reinterpret_cast<const uint8_t*>(srcPtr) + offset, + subSample.numBytesOfClearData); + offset += subSample.numBytesOfClearData; + } + } + + _hidl_cb(Status::OK, static_cast<ssize_t>(offset), ""); + return Void(); + } else if (mode == Mode::AES_CTR) { + size_t bytesDecrypted; + Status res = mSession->decrypt(keyId.data(), iv.data(), srcPtr, + static_cast<uint8_t*>(destPtr), toVector(subSamples), &bytesDecrypted); + if (res == Status::OK) { + _hidl_cb(Status::OK, static_cast<ssize_t>(bytesDecrypted), ""); + return Void(); + } else { + _hidl_cb(Status::ERROR_DRM_DECRYPT, static_cast<ssize_t>(res), + "Decryption Error"); + return Void(); + } + } else { + _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, 0, + "Selected encryption mode is not supported by the ClearKey DRM Plugin."); + return Void(); + } +} + +Return<Status> CryptoPlugin::setMediaDrmSession( + const hidl_vec<uint8_t>& sessionId) { + if (!sessionId.size()) { + mSession = nullptr; + } else { + mSession = SessionLibrary::get()->findSession(sessionId); + if (!mSession.get()) { + return Status::ERROR_DRM_SESSION_NOT_OPENED; + } + } + return Status::OK; +} + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/DrmFactory.cpp b/drm/mediadrm/plugins/clearkey/hidl/DrmFactory.cpp new file mode 100644 index 0000000000..77557f9da9 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/DrmFactory.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "hidl_ClearKeyDrmFactory" +#include <utils/Log.h> + +#include <utils/Errors.h> + +#include "DrmFactory.h" + +#include "DrmPlugin.h" +#include "ClearKeyUUID.h" +#include "MimeType.h" +#include "SessionLibrary.h" + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +using ::android::hardware::drm::V1_0::Status; +using ::android::hardware::Void; + +Return<bool> DrmFactory::isCryptoSchemeSupported( + const hidl_array<uint8_t, 16>& uuid) { + return clearkeydrm::isClearKeyUUID(uuid.data()); +} + +Return<bool> DrmFactory::isContentTypeSupported(const hidl_string &mimeType) { + // This should match the mimeTypes handed by InitDataParser. + return mimeType == kIsoBmffVideoMimeType || + mimeType == kIsoBmffAudioMimeType || + mimeType == kCencInitDataFormat || + mimeType == kWebmVideoMimeType || + mimeType == kWebmAudioMimeType || + mimeType == kWebmInitDataFormat; +} + +Return<void> DrmFactory::createPlugin( + const hidl_array<uint8_t, 16>& uuid, + const hidl_string& appPackageName, + createPlugin_cb _hidl_cb) { + UNUSED(appPackageName); + + DrmPlugin *plugin = NULL; + if (!isCryptoSchemeSupported(uuid.data())) { + ALOGE("Clear key Drm HAL: failed to create drm plugin, " \ + "invalid crypto scheme"); + _hidl_cb(Status::BAD_VALUE, plugin); + return Void(); + } + + plugin = new DrmPlugin(SessionLibrary::get()); + _hidl_cb(Status::OK, plugin); + return Void(); +} + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp new file mode 100644 index 0000000000..9506cd135d --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp @@ -0,0 +1,371 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "hidl_ClearKeyPlugin" +#include <utils/Log.h> + +#include <stdio.h> + +#include "DrmPlugin.h" +#include "ClearKeyDrmProperties.h" +#include "Session.h" +#include "TypeConvert.h" + +namespace { +const std::string kStreaming("Streaming"); +const std::string kOffline("Offline"); +const std::string kTrue("True"); + +const std::string kQueryKeyLicenseType("LicenseType"); + // Value: "Streaming" or "Offline" +const std::string kQueryKeyPlayAllowed("PlayAllowed"); + // Value: "True" or "False" +const std::string kQueryKeyRenewAllowed("RenewAllowed"); + // Value: "True" or "False" +}; + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +DrmPlugin::DrmPlugin(SessionLibrary* sessionLibrary) + : mSessionLibrary(sessionLibrary) { + mPlayPolicy.clear(); + initProperties(); + +} + +void DrmPlugin::initProperties() { + mStringProperties.clear(); + mStringProperties[kVendorKey] = kVendorValue; + mStringProperties[kVersionKey] = kVersionValue; + mStringProperties[kPluginDescriptionKey] = kPluginDescriptionValue; + mStringProperties[kAlgorithmsKey] = kAlgorithmsValue; + mStringProperties[kListenerTestSupportKey] = kListenerTestSupportValue; + + std::vector<uint8_t> valueVector; + valueVector.clear(); + valueVector.insert(valueVector.end(), + kTestDeviceIdData, kTestDeviceIdData + sizeof(kTestDeviceIdData) / sizeof(uint8_t)); + mByteArrayProperties[kDeviceIdKey] = valueVector; + + valueVector.clear(); + valueVector.insert(valueVector.end(), + kMetricsData, kMetricsData + sizeof(kMetricsData) / sizeof(uint8_t)); + mByteArrayProperties[kMetricsKey] = valueVector; +} + +Return<void> DrmPlugin::openSession(openSession_cb _hidl_cb) { + sp<Session> session = mSessionLibrary->createSession(); + std::vector<uint8_t> sessionId = session->sessionId(); + + setSecurityLevel(sessionId, SecurityLevel::SW_SECURE_CRYPTO); + _hidl_cb(Status::OK, toHidlVec(sessionId)); + return Void(); +} + +Return<Status> DrmPlugin::closeSession(const hidl_vec<uint8_t>& sessionId) { + if (sessionId.size() == 0) { + return Status::BAD_VALUE; + } + + sp<Session> session = mSessionLibrary->findSession(toVector(sessionId)); + if (session.get()) { + mSessionLibrary->destroySession(session); + return Status::OK; + } + return Status::ERROR_DRM_SESSION_NOT_OPENED; +} + +Return<void> DrmPlugin::getKeyRequest( + const hidl_vec<uint8_t>& scope, + const hidl_vec<uint8_t>& initData, + const hidl_string& mimeType, + KeyType keyType, + const hidl_vec<KeyValue>& optionalParameters, + getKeyRequest_cb _hidl_cb) { + UNUSED(optionalParameters); + + if (scope.size() == 0) { + // Returns empty keyRequest, unknown keyType and empty defaultUrl + _hidl_cb(Status::BAD_VALUE, hidl_vec<uint8_t>(), + KeyRequestType::UNKNOWN, ""); + return Void(); + } + + if (keyType != KeyType::STREAMING) { + _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, hidl_vec<uint8_t>(), + KeyRequestType::UNKNOWN, ""); + return Void(); + } + + sp<Session> session = mSessionLibrary->findSession(toVector(scope)); + if (!session.get()) { + _hidl_cb(Status::ERROR_DRM_SESSION_NOT_OPENED, hidl_vec<uint8_t>(), + KeyRequestType::UNKNOWN, ""); + return Void(); + } + + std::vector<uint8_t> request; + Status status = session->getKeyRequest(initData, mimeType, &request); + _hidl_cb(status, toHidlVec(request), KeyRequestType::INITIAL, ""); + return Void(); +} + +Return<void> DrmPlugin::getKeyRequest_1_1( + const hidl_vec<uint8_t>& scope, + const hidl_vec<uint8_t>& initData, + const hidl_string& mimeType, + KeyType keyType, + const hidl_vec<KeyValue>& optionalParameters, + getKeyRequest_1_1_cb _hidl_cb) { + hidl_string defaultUrl; + hidl_vec<uint8_t> request; + ::android::hardware::drm::V1_1::KeyRequestType requestType = + static_cast<::android::hardware::drm::V1_1::KeyRequestType>(KeyRequestType::UNKNOWN); + Status status = Status::OK; + + defaultUrl.clear(); + getKeyRequest(scope, initData, mimeType, keyType, optionalParameters, + [&](Status statusCode, const hidl_vec<uint8_t>& hResult, + KeyRequestType hKeyRequestType, + const hidl_string& hDefaultUrl) { + defaultUrl = hDefaultUrl; + request = hResult; + requestType = static_cast<::android::hardware::drm::V1_1::KeyRequestType>(hKeyRequestType); + status = statusCode; + }); + _hidl_cb(status, request, requestType, defaultUrl); + return Void(); +} + +void DrmPlugin::setPlayPolicy() { + mPlayPolicy.clear(); + + KeyValue policy; + policy.key = kQueryKeyLicenseType; + policy.value = kStreaming; + mPlayPolicy.push_back(policy); + + policy.key = kQueryKeyPlayAllowed; + policy.value = kTrue; + mPlayPolicy.push_back(policy); + + policy.key = kQueryKeyRenewAllowed; + mPlayPolicy.push_back(policy); +} + +Return<void> DrmPlugin::provideKeyResponse( + const hidl_vec<uint8_t>& scope, + const hidl_vec<uint8_t>& response, + provideKeyResponse_cb _hidl_cb) { + if (scope.size() == 0 || response.size() == 0) { + // Returns empty keySetId + _hidl_cb(Status::BAD_VALUE, hidl_vec<uint8_t>()); + return Void(); + } + sp<Session> session = mSessionLibrary->findSession(toVector(scope)); + if (!session.get()) { + _hidl_cb(Status::ERROR_DRM_SESSION_NOT_OPENED, hidl_vec<uint8_t>()); + return Void(); + } + + setPlayPolicy(); + std::vector<uint8_t> keySetId; + Status status = session->provideKeyResponse(response); + if (status == Status::OK) { + // This is for testing AMediaDrm_setOnEventListener only. + sendEvent(EventType::VENDOR_DEFINED, 0, scope); + keySetId.clear(); + } + + // Returns status and empty keySetId + _hidl_cb(status, toHidlVec(keySetId)); + return Void(); +} + +Return<void> DrmPlugin::getPropertyString( + const hidl_string& propertyName, getPropertyString_cb _hidl_cb) { + std::string name(propertyName.c_str()); + std::string value; + + if (name == "vendor") { + value = "Google"; + } else if (name == "version") { + value = "1.1"; + } else if (name == "description") { + value = "ClearKey CDM"; + } else if (name == "algorithms") { + value = ""; + } else if (name == "listenerTestSupport") { + value = mStringProperties[name]; + } else { + ALOGE("App requested unknown string property %s", name.c_str()); + _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, ""); + return Void(); + } + _hidl_cb(Status::OK, value.c_str()); + return Void(); +} + +Return<void> DrmPlugin::getPropertyByteArray( + const hidl_string& propertyName, getPropertyByteArray_cb _hidl_cb) { + std::map<std::string, std::vector<uint8_t> >::iterator itr = + mByteArrayProperties.find(std::string(propertyName.c_str())); + if (itr == mByteArrayProperties.end()) { + ALOGE("App requested unknown property: %s", propertyName.c_str()); + _hidl_cb(Status::BAD_VALUE, std::vector<uint8_t>()); + return Void(); + } + _hidl_cb(Status::OK, itr->second); + return Void(); + +} + +Return<Status> DrmPlugin::setPropertyString( + const hidl_string& name, const hidl_string& value) { + std::string immutableKeys; + immutableKeys.append(kAlgorithmsKey + ","); + immutableKeys.append(kPluginDescriptionKey + ","); + immutableKeys.append(kVendorKey + ","); + immutableKeys.append(kVersionKey + ","); + + std::string key = std::string(name.c_str()); + if (immutableKeys.find(key) != std::string::npos) { + ALOGD("Cannot set immutable property: %s", key.c_str()); + return Status::BAD_VALUE; + } + + std::map<std::string, std::string>::iterator itr = + mStringProperties.find(key); + if (itr == mStringProperties.end()) { + ALOGE("Cannot set undefined property string, key=%s", key.c_str()); + return Status::BAD_VALUE; + } + + mStringProperties[key] = std::string(value.c_str()); + return Status::OK; +} + +Return<Status> DrmPlugin::setPropertyByteArray( + const hidl_string& name, const hidl_vec<uint8_t>& value) { + UNUSED(value); + if (name == kDeviceIdKey) { + ALOGD("Cannot set immutable property: %s", name.c_str()); + return Status::BAD_VALUE; + } + + // Setting of undefined properties is not supported + ALOGE("Failed to set property byte array, key=%s", name.c_str()); + return Status::ERROR_DRM_CANNOT_HANDLE; +} + +Return<void> DrmPlugin::queryKeyStatus( + const hidl_vec<uint8_t>& sessionId, + queryKeyStatus_cb _hidl_cb) { + + if (sessionId.size() == 0) { + // Returns empty key status KeyValue pair + _hidl_cb(Status::BAD_VALUE, hidl_vec<KeyValue>()); + return Void(); + } + + std::vector<KeyValue> infoMapVec; + infoMapVec.clear(); + + KeyValue keyValuePair; + for (size_t i = 0; i < mPlayPolicy.size(); ++i) { + keyValuePair.key = mPlayPolicy[i].key; + keyValuePair.value = mPlayPolicy[i].value; + infoMapVec.push_back(keyValuePair); + } + _hidl_cb(Status::OK, toHidlVec(infoMapVec)); + return Void(); +} + +Return<void> DrmPlugin::getNumberOfSessions(getNumberOfSessions_cb _hidl_cb) { + uint32_t currentSessions = mSessionLibrary->numOpenSessions(); + uint32_t maxSessions = 10; + _hidl_cb(Status::OK, currentSessions, maxSessions); + return Void(); +} + +Return<void> DrmPlugin::getSecurityLevel(const hidl_vec<uint8_t>& sessionId, + getSecurityLevel_cb _hidl_cb) { + if (sessionId.size() == 0) { + _hidl_cb(Status::BAD_VALUE, SecurityLevel::UNKNOWN); + return Void(); + } + + std::vector<uint8_t> sid = toVector(sessionId); + sp<Session> session = mSessionLibrary->findSession(sid); + if (!session.get()) { + _hidl_cb(Status::ERROR_DRM_SESSION_NOT_OPENED, SecurityLevel::UNKNOWN); + return Void(); + } + + std::map<std::vector<uint8_t>, SecurityLevel>::iterator itr = + mSecurityLevel.find(sid); + if (itr == mSecurityLevel.end()) { + ALOGE("Session id not found"); + _hidl_cb(Status::ERROR_DRM_INVALID_STATE, SecurityLevel::UNKNOWN); + return Void(); + } + + _hidl_cb(Status::OK, itr->second); + return Void(); +} + +Return<Status> DrmPlugin::setSecurityLevel(const hidl_vec<uint8_t>& sessionId, + SecurityLevel level) { + if (sessionId.size() == 0) { + ALOGE("Invalid empty session id"); + return Status::BAD_VALUE; + } + + if (level > SecurityLevel::HW_SECURE_ALL) { + ALOGE("Cannot set invalid security level"); + return Status::BAD_VALUE; + } + + std::vector<uint8_t> sid = toVector(sessionId); + sp<Session> session = mSessionLibrary->findSession(sid); + if (!session.get()) { + return Status::ERROR_DRM_SESSION_NOT_OPENED; + } + + std::map<std::vector<uint8_t>, SecurityLevel>::iterator itr = + mSecurityLevel.find(sid); + if (itr != mSecurityLevel.end()) { + mSecurityLevel[sid] = level; + } else { + if (!mSecurityLevel.insert( + std::pair<std::vector<uint8_t>, SecurityLevel>(sid, level)).second) { + ALOGE("Failed to set security level"); + return Status::ERROR_DRM_INVALID_STATE; + } + } + return Status::OK; +} + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/InitDataParser.cpp b/drm/mediadrm/plugins/clearkey/hidl/InitDataParser.cpp new file mode 100644 index 0000000000..e2bb6517ea --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/InitDataParser.cpp @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "hidl_InitDataParser" + +#include <algorithm> +#include <utils/Log.h> + +#include "InitDataParser.h" + +#include "Base64.h" + +#include "ClearKeyUUID.h" +#include "MimeType.h" +#include "Utils.h" + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +namespace { + const size_t kKeyIdSize = 16; + const size_t kSystemIdSize = 16; +} + +std::vector<uint8_t> StrToVector(const std::string& str) { + std::vector<uint8_t> vec(str.begin(), str.end()); + return vec; +} + +Status InitDataParser::parse(const std::vector<uint8_t>& initData, + const std::string& type, + std::vector<uint8_t>* licenseRequest) { + // Build a list of the key IDs + std::vector<const uint8_t*> keyIds; + + if (type == kIsoBmffVideoMimeType || + type == kIsoBmffAudioMimeType || + type == kCencInitDataFormat) { + Status res = parsePssh(initData, &keyIds); + if (res != Status::OK) { + return res; + } + } else if (type == kWebmVideoMimeType || + type == kWebmAudioMimeType || + type == kWebmInitDataFormat) { + // WebM "init data" is just a single key ID + if (initData.size() != kKeyIdSize) { + return Status::ERROR_DRM_CANNOT_HANDLE; + } + keyIds.push_back(initData.data()); + } else { + return Status::ERROR_DRM_CANNOT_HANDLE; + } + + // Build the request + std::string requestJson = generateRequest(keyIds); + std::vector<uint8_t> requestJsonVec = StrToVector(requestJson); + + licenseRequest->clear(); + licenseRequest->insert(licenseRequest->end(), requestJsonVec.begin(), requestJsonVec.end()); + return Status::OK; +} + +Status InitDataParser::parsePssh(const std::vector<uint8_t>& initData, + std::vector<const uint8_t*>* keyIds) { + size_t readPosition = 0; + + // Validate size field + uint32_t expectedSize = initData.size(); + expectedSize = htonl(expectedSize); + if (memcmp(&initData[readPosition], &expectedSize, + sizeof(expectedSize)) != 0) { + return Status::ERROR_DRM_CANNOT_HANDLE; + } + readPosition += sizeof(expectedSize); + + // Validate PSSH box identifier + const char psshIdentifier[4] = {'p', 's', 's', 'h'}; + if (memcmp(&initData[readPosition], psshIdentifier, + sizeof(psshIdentifier)) != 0) { + return Status::ERROR_DRM_CANNOT_HANDLE; + } + readPosition += sizeof(psshIdentifier); + + // Validate EME version number + const uint8_t psshVersion1[4] = {1, 0, 0, 0}; + if (memcmp(&initData[readPosition], psshVersion1, + sizeof(psshVersion1)) != 0) { + return Status::ERROR_DRM_CANNOT_HANDLE; + } + readPosition += sizeof(psshVersion1); + + // Validate system ID + if (!clearkeydrm::isClearKeyUUID(&initData[readPosition])) { + return Status::ERROR_DRM_CANNOT_HANDLE; + } + readPosition += kSystemIdSize; + + // Read key ID count + uint32_t keyIdCount; + memcpy(&keyIdCount, &initData[readPosition], sizeof(keyIdCount)); + keyIdCount = ntohl(keyIdCount); + readPosition += sizeof(keyIdCount); + if (readPosition + ((uint64_t)keyIdCount * kKeyIdSize) != + initData.size() - sizeof(uint32_t)) { + return Status::ERROR_DRM_CANNOT_HANDLE; + } + + // Calculate the key ID offsets + for (uint32_t i = 0; i < keyIdCount; ++i) { + size_t keyIdPosition = readPosition + (i * kKeyIdSize); + keyIds->push_back(&initData[keyIdPosition]); + } + return Status::OK; +} + +std::string InitDataParser::generateRequest(const std::vector<const uint8_t*>& keyIds) { + const std::string kRequestPrefix("{\"kids\":["); + const std::string kRequestSuffix("],\"type\":\"temporary\"}"); + + std::string request(kRequestPrefix); + std::string encodedId; + for (size_t i = 0; i < keyIds.size(); ++i) { + encodedId.clear(); + encodeBase64Url(keyIds[i], kKeyIdSize, &encodedId); + if (i != 0) { + request.append(","); + } + request.push_back('\"'); + request.append(encodedId); + request.push_back('\"'); + } + request.append(kRequestSuffix); + + // Android's Base64 encoder produces padding. EME forbids padding. + const char kBase64Padding = '='; + request.erase(std::remove(request.begin(), request.end(), kBase64Padding), request.end()); + + return request; +} + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/JsonWebKey.cpp b/drm/mediadrm/plugins/clearkey/hidl/JsonWebKey.cpp new file mode 100644 index 0000000000..cccb41ecc4 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/JsonWebKey.cpp @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define LOG_TAG "hidl_JsonWebKey" + +#include <utils/Log.h> + +#include "JsonWebKey.h" + +#include "Base64.h" + +namespace { +const std::string kKeysTag("keys"); +const std::string kKeyTypeTag("kty"); +const std::string kSymmetricKeyValue("oct"); +const std::string kKeyTag("k"); +const std::string kKeyIdTag("kid"); +const std::string kBase64Padding("="); +} + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +JsonWebKey::JsonWebKey() { +} + +JsonWebKey::~JsonWebKey() { +} + +/* + * Parses a JSON Web Key Set string, initializes a KeyMap with key id:key + * pairs from the JSON Web Key Set. Both key ids and keys are base64url + * encoded. The KeyMap contains base64url decoded key id:key pairs. + * + * @return Returns false for errors, true for success. + */ +bool JsonWebKey::extractKeysFromJsonWebKeySet(const std::string& jsonWebKeySet, + KeyMap* keys) { + + keys->clear(); + + if (!parseJsonWebKeySet(jsonWebKeySet, &mJsonObjects)) { + return false; + } + + // mJsonObjects[0] contains the entire JSON Web Key Set, including + // all the base64 encoded keys. Each key is also stored separately as + // a JSON object in mJsonObjects[1..n] where n is the total + // number of keys in the set. + if (!isJsonWebKeySet(mJsonObjects[0])) { + return false; + } + + std::string encodedKey, encodedKeyId; + std::vector<uint8_t> decodedKey, decodedKeyId; + + // mJsonObjects[1] contains the first JSON Web Key in the set + for (size_t i = 1; i < mJsonObjects.size(); ++i) { + encodedKeyId.clear(); + encodedKey.clear(); + + if (!parseJsonObject(mJsonObjects[i], &mTokens)) + return false; + + if (findKey(mJsonObjects[i], &encodedKeyId, &encodedKey)) { + if (encodedKeyId.empty() || encodedKey.empty()) { + ALOGE("Must have both key id and key in the JsonWebKey set."); + continue; + } + + if (!decodeBase64String(encodedKeyId, &decodedKeyId)) { + ALOGE("Failed to decode key id(%s)", encodedKeyId.c_str()); + continue; + } + + if (!decodeBase64String(encodedKey, &decodedKey)) { + ALOGE("Failed to decode key(%s)", encodedKey.c_str()); + continue; + } + + keys->insert(std::pair<std::vector<uint8_t>, + std::vector<uint8_t> >(decodedKeyId, decodedKey)); + } + } + return true; +} + +bool JsonWebKey::decodeBase64String(const std::string& encodedText, + std::vector<uint8_t>* decodedText) { + + decodedText->clear(); + + // encodedText should not contain padding characters as per EME spec. + if (encodedText.find(kBase64Padding) != std::string::npos) { + return false; + } + + // Since decodeBase64() requires padding characters, + // add them so length of encodedText is exactly a multiple of 4. + int remainder = encodedText.length() % 4; + std::string paddedText(encodedText); + if (remainder > 0) { + for (int i = 0; i < 4 - remainder; ++i) { + paddedText.append(kBase64Padding); + } + } + + sp<Buffer> buffer = decodeBase64(paddedText); + if (buffer == nullptr) { + ALOGE("Malformed base64 encoded content found."); + return false; + } + + decodedText->insert(decodedText->end(), buffer->base(), buffer->base() + buffer->size()); + return true; +} + +bool JsonWebKey::findKey(const std::string& jsonObject, std::string* keyId, + std::string* encodedKey) { + + std::string key, value; + + // Only allow symmetric key, i.e. "kty":"oct" pair. + if (jsonObject.find(kKeyTypeTag) != std::string::npos) { + findValue(kKeyTypeTag, &value); + if (0 != value.compare(kSymmetricKeyValue)) + return false; + } + + if (jsonObject.find(kKeyIdTag) != std::string::npos) { + findValue(kKeyIdTag, keyId); + } + + if (jsonObject.find(kKeyTag) != std::string::npos) { + findValue(kKeyTag, encodedKey); + } + return true; +} + +void JsonWebKey::findValue(const std::string &key, std::string* value) { + value->clear(); + const char* valueToken; + for (std::vector<std::string>::const_iterator nextToken = mTokens.begin(); + nextToken != mTokens.end(); ++nextToken) { + if (0 == (*nextToken).compare(key)) { + if (nextToken + 1 == mTokens.end()) + break; + valueToken = (*(nextToken + 1)).c_str(); + value->assign(valueToken); + nextToken++; + break; + } + } +} + +bool JsonWebKey::isJsonWebKeySet(const std::string& jsonObject) const { + if (jsonObject.find(kKeysTag) == std::string::npos) { + ALOGE("JSON Web Key does not contain keys."); + return false; + } + return true; +} + +/* + * Parses a JSON objects string and initializes a vector of tokens. + * + * @return Returns false for errors, true for success. + */ +bool JsonWebKey::parseJsonObject(const std::string& jsonObject, + std::vector<std::string>* tokens) { + jsmn_parser parser; + + jsmn_init(&parser); + int numTokens = jsmn_parse(&parser, + jsonObject.c_str(), jsonObject.size(), nullptr, 0); + if (numTokens < 0) { + ALOGE("Parser returns error code=%d", numTokens); + return false; + } + + unsigned int jsmnTokensSize = numTokens * sizeof(jsmntok_t); + mJsmnTokens.clear(); + mJsmnTokens.resize(jsmnTokensSize); + + jsmn_init(&parser); + int status = jsmn_parse(&parser, jsonObject.c_str(), + jsonObject.size(), mJsmnTokens.data(), numTokens); + if (status < 0) { + ALOGE("Parser returns error code=%d", status); + return false; + } + + tokens->clear(); + std::string token; + const char *pjs; + for (int j = 0; j < numTokens; ++j) { + pjs = jsonObject.c_str() + mJsmnTokens[j].start; + if (mJsmnTokens[j].type == JSMN_STRING || + mJsmnTokens[j].type == JSMN_PRIMITIVE) { + token.assign(pjs, mJsmnTokens[j].end - mJsmnTokens[j].start); + tokens->push_back(token); + } + } + return true; +} + +/* + * Parses JSON Web Key Set string and initializes a vector of JSON objects. + * + * @return Returns false for errors, true for success. + */ +bool JsonWebKey::parseJsonWebKeySet(const std::string& jsonWebKeySet, + std::vector<std::string>* jsonObjects) { + if (jsonWebKeySet.empty()) { + ALOGE("Empty JSON Web Key"); + return false; + } + + // The jsmn parser only supports unicode encoding. + jsmn_parser parser; + + // Computes number of tokens. A token marks the type, offset in + // the original string. + jsmn_init(&parser); + int numTokens = jsmn_parse(&parser, + jsonWebKeySet.c_str(), jsonWebKeySet.size(), nullptr, 0); + if (numTokens < 0) { + ALOGE("Parser returns error code=%d", numTokens); + return false; + } + + unsigned int jsmnTokensSize = numTokens * sizeof(jsmntok_t); + mJsmnTokens.resize(jsmnTokensSize); + + jsmn_init(&parser); + int status = jsmn_parse(&parser, jsonWebKeySet.c_str(), + jsonWebKeySet.size(), mJsmnTokens.data(), numTokens); + if (status < 0) { + ALOGE("Parser returns error code=%d", status); + return false; + } + + std::string token; + const char *pjs; + for (int i = 0; i < numTokens; ++i) { + pjs = jsonWebKeySet.c_str() + mJsmnTokens[i].start; + if (mJsmnTokens[i].type == JSMN_OBJECT) { + token.assign(pjs, mJsmnTokens[i].end - mJsmnTokens[i].start); + jsonObjects->push_back(token); + } + } + return true; +} + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android + diff --git a/drm/mediadrm/plugins/clearkey/hidl/Session.cpp b/drm/mediadrm/plugins/clearkey/hidl/Session.cpp new file mode 100644 index 0000000000..07c9269c68 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/Session.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "hidl_ClearKeySession" +#include <utils/Log.h> + +#include "Session.h" +#include "Utils.h" + +#include "AesCtrDecryptor.h" +#include "InitDataParser.h" +#include "JsonWebKey.h" + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +using ::android::hardware::drm::V1_0::KeyValue; +using ::android::hardware::drm::V1_0::Status; +using ::android::hardware::drm::V1_0::SubSample; +using ::android::hardware::Return; +using ::android::sp; + +using android::Mutex; + +Status Session::getKeyRequest( + const std::vector<uint8_t>& initData, + const std::string& mimeType, + std::vector<uint8_t>* keyRequest) const { + InitDataParser parser; + return parser.parse(initData, mimeType, keyRequest); +} + +Status Session::provideKeyResponse(const std::vector<uint8_t>& response) { + std::string responseString( + reinterpret_cast<const char*>(response.data()), response.size()); + KeyMap keys; + + Mutex::Autolock lock(mMapLock); + JsonWebKey parser; + if (parser.extractKeysFromJsonWebKeySet(responseString, &keys)) { + for (auto &key : keys) { + std::string first(key.first.begin(), key.first.end()); + std::string second(key.second.begin(), key.second.end()); + mKeyMap.insert(std::pair<std::vector<uint8_t>, + std::vector<uint8_t> >(key.first, key.second)); + } + return Status::OK; + } else { + return Status::ERROR_DRM_UNKNOWN; + } +} + +Status Session::decrypt( + const KeyId keyId, const Iv iv, const uint8_t* srcPtr, + uint8_t* destPtr, const std::vector<SubSample> subSamples, + size_t* bytesDecryptedOut) { + Mutex::Autolock lock(mMapLock); + + std::vector<uint8_t> keyIdVector; + keyIdVector.clear(); + keyIdVector.insert(keyIdVector.end(), keyId, keyId + kBlockSize); + std::map<std::vector<uint8_t>, std::vector<uint8_t> >::iterator itr; + itr = mKeyMap.find(keyIdVector); + if (itr == mKeyMap.end()) { + return Status::ERROR_DRM_NO_LICENSE; + } + + AesCtrDecryptor decryptor; + return decryptor.decrypt( + itr->second /*key*/, iv, srcPtr, destPtr, subSamples, + subSamples.size(), bytesDecryptedOut); +} + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/SessionLibrary.cpp b/drm/mediadrm/plugins/clearkey/hidl/SessionLibrary.cpp new file mode 100644 index 0000000000..b4319e6a45 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/SessionLibrary.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "hidl_ClearKeySessionLibrary" +#include <utils/Log.h> + +#include "SessionLibrary.h" +#include "Utils.h" + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +using ::android::hardware::hidl_string; +using ::android::hardware::hidl_vec; +using ::android::sp; + +Mutex SessionLibrary::sSingletonLock; +SessionLibrary* SessionLibrary::sSingleton = NULL; + +SessionLibrary* SessionLibrary::get() { + Mutex::Autolock lock(sSingletonLock); + + if (sSingleton == NULL) { + ALOGD("Instantiating Session Library Singleton."); + sSingleton = new SessionLibrary(); + } + + return sSingleton; +} + +sp<Session> SessionLibrary::createSession() { + Mutex::Autolock lock(mSessionsLock); + + char sessionIdRaw[16]; + snprintf(sessionIdRaw, sizeof(sessionIdRaw), "%u", mNextSessionId); + + mNextSessionId += 1; + + std::vector<uint8_t> sessionId; + sessionId.insert(sessionId.end(), sessionIdRaw, + sessionIdRaw + sizeof(sessionIdRaw) / sizeof(uint8_t)); + + mSessions.insert(std::pair<std::vector<uint8_t>, + sp<Session> >(sessionId, new Session(sessionId))); + std::map<std::vector<uint8_t>, sp<Session> >::iterator itr = mSessions.find(sessionId); + if (itr != mSessions.end()) { + return itr->second; + } else { + return nullptr; + } +} + +sp<Session> SessionLibrary::findSession( + const std::vector<uint8_t>& sessionId) { + Mutex::Autolock lock(mSessionsLock); + std::map<std::vector<uint8_t>, sp<Session> >::iterator itr = mSessions.find(sessionId); + if (itr != mSessions.end()) { + return itr->second; + } else { + return nullptr; + } +} + +void SessionLibrary::destroySession(const sp<Session>& session) { + Mutex::Autolock lock(mSessionsLock); + mSessions.erase(session->sessionId()); +} + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.1-service.clearkey.rc b/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.1-service.clearkey.rc new file mode 100644 index 0000000000..ffe856a55d --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.1-service.clearkey.rc @@ -0,0 +1,6 @@ +service vendor.drm-clearkey-hal-1-1 /vendor/bin/hw/android.hardware.drm@1.1-service.clearkey + class hal + user media + group media mediadrm + ioprio rt 4 + writepid /dev/cpuset/foreground/tasks diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/AesCtrDecryptor.h b/drm/mediadrm/plugins/clearkey/hidl/include/AesCtrDecryptor.h new file mode 100644 index 0000000000..0c7ef20fdf --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/include/AesCtrDecryptor.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CLEARKEY_AES_CTR_DECRYPTOR_H_ +#define CLEARKEY_AES_CTR_DECRYPTOR_H_ + +#include "ClearKeyTypes.h" + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +using ::android::hardware::drm::V1_0::Status; +using ::android::hardware::drm::V1_0::SubSample; + +class AesCtrDecryptor { +public: + AesCtrDecryptor() {} + + Status decrypt(const std::vector<uint8_t>& key, const Iv iv, + const uint8_t* source, uint8_t* destination, + const std::vector<SubSample> subSamples, size_t numSubSamples, + size_t* bytesDecryptedOut); + +private: + CLEARKEY_DISALLOW_COPY_AND_ASSIGN(AesCtrDecryptor); +}; + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android + +#endif // CLEARKEY_AES_CTR_DECRYPTOR_H_ diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/Base64.h b/drm/mediadrm/plugins/clearkey/hidl/include/Base64.h new file mode 100644 index 0000000000..4a385bdb6e --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/include/Base64.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef BASE_64_H_ + +#define BASE_64_H_ + +#include <android/hardware/drm/1.0/types.h> + +#include "Buffer.h" + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +using ::android::sp; + +struct Buffer; + +sp<Buffer> decodeBase64(const std::string &s); +void encodeBase64(const void *data, size_t size, std::string *out); + +void encodeBase64Url(const void *data, size_t size, std::string *out); + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android + +#endif // BASE_64_H_ diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/Buffer.h b/drm/mediadrm/plugins/clearkey/hidl/include/Buffer.h new file mode 100644 index 0000000000..5bbb28aa14 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/include/Buffer.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef BUFFER_H_ +#define BUFFER_H_ + +#include <android/hardware/drm/1.0/types.h> +#include <utils/RefBase.h> + +#include "ClearKeyTypes.h" + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +using ::android::sp; + +struct Buffer : public RefBase { + explicit Buffer(size_t capacity); + + uint8_t *base() { return reinterpret_cast<uint8_t *>(mData); } + uint8_t *data() { return reinterpret_cast<uint8_t *>(mData) + mRangeOffset; } + size_t capacity() const { return mCapacity; } + size_t size() const { return mRangeLength; } + size_t offset() const { return mRangeOffset; } + +protected: + virtual ~Buffer(); + +private: + void *mData; + size_t mCapacity; + size_t mRangeOffset; + size_t mRangeLength; + + bool mOwnsData; + + CLEARKEY_DISALLOW_COPY_AND_ASSIGN(Buffer); +}; + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android + +#endif // BUFFER_H_ diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyDrmProperties.h b/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyDrmProperties.h new file mode 100644 index 0000000000..7731b14f82 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyDrmProperties.h @@ -0,0 +1,54 @@ +/* + * 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 CLEARKEY_DRM_PROPERTIES_H_ +#define CLEARKEY_DRM_PROPERTIES_H_ + +#include <string.h> + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +static const std::string kVendorKey("vendor"); +static const std::string kVendorValue("Google"); +static const std::string kVersionKey("version"); +static const std::string kVersionValue("1.0"); +static const std::string kPluginDescriptionKey("description"); +static const std::string kPluginDescriptionValue("ClearKey CDM"); +static const std::string kAlgorithmsKey("algorithms"); +static const std::string kAlgorithmsValue(""); +static const std::string kListenerTestSupportKey("listenerTestSupport"); +static const std::string kListenerTestSupportValue("true"); + +static const std::string kDeviceIdKey("deviceId"); +static const uint8_t kTestDeviceIdData[] = + {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}; +// TODO stub out metrics for nw +static const std::string kMetricsKey("metrics"); +static const uint8_t kMetricsData[] = { 0 }; + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android + +#endif // CLEARKEY_DRM_PROPERTIES_H_ + diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyTypes.h b/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyTypes.h new file mode 100644 index 0000000000..46cb5e4dd5 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyTypes.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CLEARKEY_MACROS_H_ +#define CLEARKEY_MACROS_H_ + +#include <android/hardware/drm/1.0/types.h> + +#include <map> + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +using ::android::hardware::drm::V1_0::KeyValue; +using ::android::hardware::hidl_vec; + +const uint8_t kBlockSize = 16; //AES_BLOCK_SIZE; +typedef uint8_t KeyId[kBlockSize]; +typedef uint8_t Iv[kBlockSize]; + +typedef ::android::hardware::drm::V1_0::SubSample SubSample; +typedef std::map<std::vector<uint8_t>, std::vector<uint8_t> > KeyMap; + +#define CLEARKEY_DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&) = delete; \ + void operator=(const TypeName&) = delete; + +#define CLEARKEY_DISALLOW_COPY_AND_ASSIGN_AND_NEW(TypeName) \ + TypeName() = delete; \ + TypeName(const TypeName&) = delete; \ + void operator=(const TypeName&) = delete; + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android + +#endif // CLEARKEY_MACROS_H_ diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/CreatePluginFactories.h b/drm/mediadrm/plugins/clearkey/hidl/include/CreatePluginFactories.h new file mode 100644 index 0000000000..99520277f2 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/include/CreatePluginFactories.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CLEARKEY_CREATE_PLUGIN_FACTORIES_H_ +#define CLEARKEY_CREATE_PLUGIN_FACTORIES_H_ + +#include <android/hardware/drm/1.1/ICryptoFactory.h> +#include <android/hardware/drm/1.1/IDrmFactory.h> + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +using ::android::hardware::drm::V1_1::ICryptoFactory; +using ::android::hardware::drm::V1_1::IDrmFactory; + +extern "C" { + IDrmFactory* createDrmFactory(); + ICryptoFactory* createCryptoFactory(); +} + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android +#endif // CLEARKEY_CREATE_PLUGIN_FACTORIES_H_ diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/CryptoFactory.h b/drm/mediadrm/plugins/clearkey/hidl/include/CryptoFactory.h new file mode 100644 index 0000000000..175ab761fa --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/include/CryptoFactory.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CLEARKEY_CRYPTO_FACTORY_H_ +#define CLEARKEY_CRYPTO_FACTORY_H_ + +#include <android/hardware/drm/1.0/ICryptoPlugin.h> +#include <android/hardware/drm/1.1/ICryptoFactory.h> + +#include "ClearKeyTypes.h" + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +using ::android::hardware::drm::V1_1::ICryptoFactory; +using ::android::hardware::drm::V1_0::ICryptoPlugin; +using ::android::hardware::hidl_array; +using ::android::hardware::hidl_string; +using ::android::hardware::Return; + +struct CryptoFactory : public ICryptoFactory { + CryptoFactory() {} + virtual ~CryptoFactory() {} + + Return<bool> isCryptoSchemeSupported(const hidl_array<uint8_t, 16>& uuid) + override; + + Return<void> createPlugin( + const hidl_array<uint8_t, 16>& uuid, + const hidl_vec<uint8_t>& initData, + createPlugin_cb _hidl_cb) override; + +private: + CLEARKEY_DISALLOW_COPY_AND_ASSIGN(CryptoFactory); + +}; + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android + +#endif // CLEARKEY_CRYPTO_FACTORY_H_ diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/CryptoPlugin.h b/drm/mediadrm/plugins/clearkey/hidl/include/CryptoPlugin.h new file mode 100644 index 0000000000..6a738066d0 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/include/CryptoPlugin.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CLEARKEY_CRYPTO_PLUGIN_H_ +#define CLEARKEY_CRYPTO_PLUGIN_H_ + +#include <android/hardware/drm/1.0/ICryptoPlugin.h> +#include <android/hidl/memory/1.0/IMemory.h> + +#include "ClearKeyTypes.h" +#include "Session.h" +#include "Utils.h" + +namespace { + static const size_t KEY_ID_SIZE = 16; + static const size_t KEY_IV_SIZE = 16; +} + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +using ::android::hardware::drm::V1_0::DestinationBuffer; +using ::android::hardware::drm::V1_0::ICryptoPlugin; +using ::android::hardware::drm::V1_0::Mode; +using ::android::hardware::drm::V1_0::Pattern; +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::hardware::Void; +using ::android::hidl::memory::V1_0::IMemory; +using ::android::sp; + +struct CryptoPlugin : public ICryptoPlugin { + explicit CryptoPlugin(const hidl_vec<uint8_t>& sessionId) { + mInitStatus = setMediaDrmSession(sessionId); + } + virtual ~CryptoPlugin() {} + + Return<bool> requiresSecureDecoderComponent(const hidl_string& mime) { + UNUSED(mime); + return false; + } + + Return<void> notifyResolution(uint32_t width, uint32_t height) { + UNUSED(width); + UNUSED(height); + return Void(); + } + + Return<void> decrypt( + bool secure, + const hidl_array<uint8_t, KEY_ID_SIZE>& keyId, + const hidl_array<uint8_t, KEY_IV_SIZE>& iv, + Mode mode, + const Pattern& pattern, + const hidl_vec<SubSample>& subSamples, + const SharedBuffer& source, + uint64_t offset, + const DestinationBuffer& destination, + decrypt_cb _hidl_cb); + + Return<void> setSharedBufferBase(const hidl_memory& base, + uint32_t bufferId); + + Return<Status> setMediaDrmSession(const hidl_vec<uint8_t>& sessionId); + + Return<Status> getInitStatus() const { return mInitStatus; } + +private: + CLEARKEY_DISALLOW_COPY_AND_ASSIGN(CryptoPlugin); + + std::map<uint32_t, sp<IMemory> > mSharedBufferMap; + sp<Session> mSession; + Status mInitStatus; +}; + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android + +#endif // CLEARKEY_CRYPTO_PLUGIN_H_ diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/DrmFactory.h b/drm/mediadrm/plugins/clearkey/hidl/include/DrmFactory.h new file mode 100644 index 0000000000..6f581955b5 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/include/DrmFactory.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CLEARKEY_DRM_FACTORY_H_ +#define CLEARKEY_DRM_FACTORY_H_ + +#include <android/hardware/drm/1.1/IDrmPlugin.h> +#include <android/hardware/drm/1.1/IDrmFactory.h> + +#include "ClearKeyTypes.h" + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +using ::android::hardware::hidl_array; +using ::android::hardware::hidl_string; +using ::android::hardware::Return; + +struct DrmFactory : public IDrmFactory { + DrmFactory() {} + virtual ~DrmFactory() {} + + Return<bool> isCryptoSchemeSupported(const hidl_array<uint8_t, 16>& uuid) + override; + + Return<bool> isContentTypeSupported(const hidl_string &mimeType) + override; + + Return<void> createPlugin( + const hidl_array<uint8_t, 16>& uuid, + const hidl_string& appPackageName, + createPlugin_cb _hidl_cb) override; + +private: + CLEARKEY_DISALLOW_COPY_AND_ASSIGN(DrmFactory); +}; + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android + +#endif // CLEARKEY_DRM_FACTORY_H_ diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h b/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h new file mode 100644 index 0000000000..5ae9833be4 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h @@ -0,0 +1,353 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CLEARKEY_DRM_PLUGIN_H_ +#define CLEARKEY_DRM_PLUGIN_H_ + + +#include <android/hardware/drm/1.1/IDrmPlugin.h> + +#include "SessionLibrary.h" +#include "Utils.h" + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +using ::android::hardware::drm::V1_0::EventType; +using ::android::hardware::drm::V1_0::IDrmPluginListener; +using ::android::hardware::drm::V1_0::KeyRequestType; +using ::android::hardware::drm::V1_0::KeyStatus; +using ::android::hardware::drm::V1_0::KeyType; +using ::android::hardware::drm::V1_0::KeyValue; +using ::android::hardware::drm::V1_0::SecureStop; +using ::android::hardware::drm::V1_0::SecureStopId; +using ::android::hardware::drm::V1_0::Status; + +using ::android::hardware::hidl_string; +using ::android::hardware::hidl_vec; +using ::android::hardware::Return; +using ::android::hardware::Void; +using ::android::sp; + +struct DrmPlugin : public IDrmPlugin { + explicit DrmPlugin(SessionLibrary* sessionLibrary); + + virtual ~DrmPlugin() {} + + Return<void> openSession(openSession_cb _hidl_cb) override; + + Return<Status> closeSession(const hidl_vec<uint8_t>& sessionId) override; + + Return<void> getKeyRequest( + const hidl_vec<uint8_t>& scope, + const hidl_vec<uint8_t>& initData, + const hidl_string& mimeType, + KeyType keyType, + const hidl_vec<KeyValue>& optionalParameters, + getKeyRequest_cb _hidl_cb) override; + + Return<void> getKeyRequest_1_1( + const hidl_vec<uint8_t>& scope, + const hidl_vec<uint8_t>& initData, + const hidl_string& mimeType, + KeyType keyType, + const hidl_vec<KeyValue>& optionalParameters, + getKeyRequest_1_1_cb _hidl_cb) override; + + Return<void> provideKeyResponse( + const hidl_vec<uint8_t>& scope, + const hidl_vec<uint8_t>& response, + provideKeyResponse_cb _hidl_cb) override; + + Return<Status> removeKeys(const hidl_vec<uint8_t>& sessionId) { + if (sessionId.size() == 0) { + return Status::BAD_VALUE; + } + return Status::ERROR_DRM_CANNOT_HANDLE; + } + + Return<Status> restoreKeys( + const hidl_vec<uint8_t>& sessionId, + const hidl_vec<uint8_t>& keySetId) { + + if (sessionId.size() == 0 || keySetId.size() == 0) { + return Status::BAD_VALUE; + } + return Status::ERROR_DRM_CANNOT_HANDLE; + } + + Return<void> queryKeyStatus( + const hidl_vec<uint8_t>& sessionId, + queryKeyStatus_cb _hidl_cb) override; + + Return<void> getProvisionRequest( + const hidl_string& certificateType, + const hidl_string& certificateAuthority, + getProvisionRequest_cb _hidl_cb) { + UNUSED(certificateType); + UNUSED(certificateAuthority); + + hidl_string defaultUrl; + _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, hidl_vec<uint8_t>(), defaultUrl); + return Void(); + } + + Return<void> provideProvisionResponse( + const hidl_vec<uint8_t>& response, + provideProvisionResponse_cb _hidl_cb) { + + if (response.size() == 0) { + _hidl_cb(Status::BAD_VALUE, hidl_vec<uint8_t>(), hidl_vec<uint8_t>()); + return Void(); + } + _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, hidl_vec<uint8_t>(), hidl_vec<uint8_t>()); + return Void(); + } + + Return<void> getSecureStops(getSecureStops_cb _hidl_cb) { + _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, hidl_vec<SecureStop>()); + return Void(); + } + + Return<void> getSecureStop( + const hidl_vec<uint8_t>& secureStopId, + getSecureStop_cb _hidl_cb) { + + if (secureStopId.size() == 0) { + _hidl_cb(Status::BAD_VALUE, SecureStop()); + return Void(); + } + _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, SecureStop()); + return Void(); + } + + Return<Status> releaseSecureStop(const hidl_vec<uint8_t>& ssRelease) { + if (ssRelease.size() == 0) { + return Status::BAD_VALUE; + } + return Status::ERROR_DRM_CANNOT_HANDLE; + } + + Return<Status> releaseAllSecureStops() { + return Status::ERROR_DRM_CANNOT_HANDLE; + } + + Return<void> getHdcpLevels(getHdcpLevels_cb _hidl_cb) { + HdcpLevel connectedLevel = HdcpLevel::HDCP_NONE; + HdcpLevel maxLevel = HdcpLevel::HDCP_NO_OUTPUT; + _hidl_cb(Status::OK, connectedLevel, maxLevel); + return Void(); + } + + Return<void> getNumberOfSessions(getNumberOfSessions_cb _hidl_cb) override; + + Return<void> getSecurityLevel(const hidl_vec<uint8_t>& sessionId, + getSecurityLevel_cb _hidl_cb) override; + + Return<Status> setSecurityLevel(const hidl_vec<uint8_t>& sessionId, + SecurityLevel level) override; + + Return<void> getPropertyString( + const hidl_string& name, + getPropertyString_cb _hidl_cb) override; + + Return<void> getPropertyByteArray( + const hidl_string& name, + getPropertyByteArray_cb _hidl_cb) override; + + Return<Status> setPropertyString( + const hidl_string& name, const hidl_string& value) override; + + Return<Status> setPropertyByteArray( + const hidl_string& name, const hidl_vec<uint8_t>& value) override; + + Return<Status> setCipherAlgorithm( + const hidl_vec<uint8_t>& sessionId, const hidl_string& algorithm) { + if (sessionId.size() == 0 || algorithm.size() == 0) { + return Status::BAD_VALUE; + } + return Status::ERROR_DRM_CANNOT_HANDLE; + } + + Return<Status> setMacAlgorithm( + const hidl_vec<uint8_t>& sessionId, const hidl_string& algorithm) { + if (sessionId.size() == 0 || algorithm.size() == 0) { + return Status::BAD_VALUE; + } + return Status::ERROR_DRM_CANNOT_HANDLE; + } + + Return<void> encrypt( + const hidl_vec<uint8_t>& sessionId, + const hidl_vec<uint8_t>& keyId, + const hidl_vec<uint8_t>& input, + const hidl_vec<uint8_t>& iv, + encrypt_cb _hidl_cb) { + if (sessionId.size() == 0 || keyId.size() == 0 || + input.size() == 0 || iv.size() == 0) { + _hidl_cb(Status::BAD_VALUE, hidl_vec<uint8_t>()); + return Void(); + } + _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, hidl_vec<uint8_t>()); + return Void(); + } + + Return<void> decrypt( + const hidl_vec<uint8_t>& sessionId, + const hidl_vec<uint8_t>& keyId, + const hidl_vec<uint8_t>& input, + const hidl_vec<uint8_t>& iv, + decrypt_cb _hidl_cb) { + if (sessionId.size() == 0 || keyId.size() == 0 || + input.size() == 0 || iv.size() == 0) { + _hidl_cb(Status::BAD_VALUE, hidl_vec<uint8_t>()); + return Void(); + } + _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, hidl_vec<uint8_t>()); + return Void(); + } + + Return<void> sign( + const hidl_vec<uint8_t>& sessionId, + const hidl_vec<uint8_t>& keyId, + const hidl_vec<uint8_t>& message, + sign_cb _hidl_cb) { + if (sessionId.size() == 0 || keyId.size() == 0 || + message.size() == 0) { + _hidl_cb(Status::BAD_VALUE, hidl_vec<uint8_t>()); + return Void(); + } + _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, hidl_vec<uint8_t>()); + return Void(); + } + + Return<void> verify( + const hidl_vec<uint8_t>& sessionId, + const hidl_vec<uint8_t>& keyId, + const hidl_vec<uint8_t>& message, + const hidl_vec<uint8_t>& signature, + verify_cb _hidl_cb) { + + if (sessionId.size() == 0 || keyId.size() == 0 || + message.size() == 0 || signature.size() == 0) { + _hidl_cb(Status::BAD_VALUE, false); + return Void(); + } + _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, false); + return Void(); + } + + Return<void> signRSA( + const hidl_vec<uint8_t>& sessionId, + const hidl_string& algorithm, + const hidl_vec<uint8_t>& message, + const hidl_vec<uint8_t>& wrappedKey, + signRSA_cb _hidl_cb) { + if (sessionId.size() == 0 || algorithm.size() == 0 || + message.size() == 0 || wrappedKey.size() == 0) { + _hidl_cb(Status::BAD_VALUE, hidl_vec<uint8_t>()); + return Void(); + } + _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, hidl_vec<uint8_t>()); + return Void(); + } + + Return<void> setListener(const sp<IDrmPluginListener>& listener) { + mListener = listener; + return Void(); + }; + + Return<void> sendEvent(EventType eventType, const hidl_vec<uint8_t>& sessionId, + const hidl_vec<uint8_t>& data) { + if (mListener != NULL) { + mListener->sendEvent(eventType, sessionId, data); + } else { + ALOGE("Null event listener, event not sent"); + } + return Void(); + } + + Return<void> sendExpirationUpdate(const hidl_vec<uint8_t>& sessionId, int64_t expiryTimeInMS) { + if (mListener != NULL) { + mListener->sendExpirationUpdate(sessionId, expiryTimeInMS); + } else { + ALOGE("Null event listener, event not sent"); + } + return Void(); + } + + Return<void> sendKeysChange(const hidl_vec<uint8_t>& sessionId, + const hidl_vec<KeyStatus>& keyStatusList, bool hasNewUsableKey) { + if (mListener != NULL) { + mListener->sendKeysChange(sessionId, keyStatusList, hasNewUsableKey); + } else { + ALOGE("Null event listener, event not sent"); + } + return Void(); + } + + Return<void> getMetrics(getMetrics_cb _hidl_cb) { + _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, hidl_vec<DrmMetricGroup>()); + return Void(); + } + + Return<void> getSecureStopIds(getSecureStopIds_cb _hidl_cb) { + _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, hidl_vec<SecureStopId>()); + return Void(); + } + + Return<Status> releaseSecureStops(const SecureStopRelease& ssRelease) { + if (ssRelease.opaqueData.size() == 0) { + return Status::BAD_VALUE; + } + return Status::ERROR_DRM_CANNOT_HANDLE; + } + + Return<Status> removeSecureStop(const hidl_vec<uint8_t>& secureStopId) { + if (secureStopId.size() == 0) { + return Status::BAD_VALUE; + } + return Status::ERROR_DRM_CANNOT_HANDLE; + } + + Return<Status> removeAllSecureStops() { + return Status::ERROR_DRM_CANNOT_HANDLE; + } + +private: + void initProperties(); + void setPlayPolicy(); + + std::vector<KeyValue> mPlayPolicy; + std::map<std::string, std::string> mStringProperties; + std::map<std::string, std::vector<uint8_t> > mByteArrayProperties; + std::map<std::vector<uint8_t>, SecurityLevel> mSecurityLevel; + sp<IDrmPluginListener> mListener; + SessionLibrary *mSessionLibrary; + + CLEARKEY_DISALLOW_COPY_AND_ASSIGN_AND_NEW(DrmPlugin); +}; + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android + +#endif // CLEARKEY_DRM_PLUGIN_H_ diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/InitDataParser.h b/drm/mediadrm/plugins/clearkey/hidl/include/InitDataParser.h new file mode 100644 index 0000000000..3189c4aeb2 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/include/InitDataParser.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CLEARKEY_INIT_DATA_PARSER_H_ +#define CLEARKEY_INIT_DATA_PARSER_H_ + +#include <android/hardware/drm/1.0/types.h> + +#include "ClearKeyTypes.h" + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +using ::android::hardware::drm::V1_0::Status; + +class InitDataParser { +public: + InitDataParser() {} + + Status parse(const std::vector<uint8_t>& initData, + const std::string& type, + std::vector<uint8_t>* licenseRequest); + +private: + CLEARKEY_DISALLOW_COPY_AND_ASSIGN(InitDataParser); + + Status parsePssh(const std::vector<uint8_t>& initData, + std::vector<const uint8_t*>* keyIds); + + std::string generateRequest( + const std::vector<const uint8_t*>& keyIds); +}; + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android + +#endif // CLEARKEY_INIT_DATA_PARSER_H_ diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/JsonWebKey.h b/drm/mediadrm/plugins/clearkey/hidl/include/JsonWebKey.h new file mode 100644 index 0000000000..4ab034cd74 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/include/JsonWebKey.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CLEARKEY_JSON_WEB_KEY_H_ +#define CLEARKEY_JSON_WEB_KEY_H_ + +#include "jsmn.h" +#include "Utils.h" +#include "ClearKeyTypes.h" + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +class JsonWebKey { + public: + JsonWebKey(); + virtual ~JsonWebKey(); + + bool extractKeysFromJsonWebKeySet(const std::string& jsonWebKeySet, + KeyMap* keys); + + private: + std::vector<jsmntok_t> mJsmnTokens; + std::vector<std::string> mJsonObjects; + std::vector<std::string> mTokens; + + bool decodeBase64String(const std::string& encodedText, + std::vector<uint8_t>* decodedText); + bool findKey(const std::string& jsonObject, std::string* keyId, + std::string* encodedKey); + void findValue(const std::string &key, std::string* value); + bool isJsonWebKeySet(const std::string& jsonObject) const; + bool parseJsonObject(const std::string& jsonObject, + std::vector<std::string>* tokens); + bool parseJsonWebKeySet(const std::string& jsonWebKeySet, + std::vector<std::string>* jsonObjects); + + CLEARKEY_DISALLOW_COPY_AND_ASSIGN(JsonWebKey); +}; + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android + +#endif // CLEARKEY_JSON_WEB_KEY_H_ diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/Session.h b/drm/mediadrm/plugins/clearkey/hidl/include/Session.h new file mode 100644 index 0000000000..cddfca5eba --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/include/Session.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CLEARKEY_SESSION_H_ +#define CLEARKEY_SESSION_H_ + +#include <utils/Mutex.h> +#include <utils/RefBase.h> +#include <vector> + +#include "ClearKeyTypes.h" + + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +using ::android::hardware::drm::V1_0::Status; +using ::android::hardware::drm::V1_0::SubSample; + +class Session : public RefBase { +public: + explicit Session(const std::vector<uint8_t>& sessionId) + : mSessionId(sessionId) {} + virtual ~Session() {} + + const std::vector<uint8_t>& sessionId() const { return mSessionId; } + + Status getKeyRequest( + const std::vector<uint8_t>& mimeType, + const std::string& initDataType, + std::vector<uint8_t>* keyRequest) const; + + Status provideKeyResponse( + const std::vector<uint8_t>& response); + + Status decrypt( + const KeyId keyId, const Iv iv, const uint8_t* srcPtr, + uint8_t* dstPtr, const std::vector<SubSample> subSamples, + size_t* bytesDecryptedOut); + +private: + CLEARKEY_DISALLOW_COPY_AND_ASSIGN(Session); + + const std::vector<uint8_t> mSessionId; + KeyMap mKeyMap; + Mutex mMapLock; +}; + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android + +#endif // CLEARKEY_SESSION_H_ diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/SessionLibrary.h b/drm/mediadrm/plugins/clearkey/hidl/include/SessionLibrary.h new file mode 100644 index 0000000000..326a0c100d --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/include/SessionLibrary.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CLEARKEY_SESSION_LIBRARY_H_ +#define CLEARKEY_SESSION_LIBRARY_H_ + +#include <utils/RefBase.h> +#include <utils/Mutex.h> + +#include "ClearKeyTypes.h" +#include "Session.h" + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +using ::android::sp; + +class SessionLibrary : public RefBase { +public: + static SessionLibrary* get(); + + sp<Session> createSession(); + + sp<Session> findSession( + const std::vector<uint8_t>& sessionId); + + void destroySession(const sp<Session>& session); + + size_t numOpenSessions() const { return mSessions.size(); } + +private: + CLEARKEY_DISALLOW_COPY_AND_ASSIGN(SessionLibrary); + + SessionLibrary() : mNextSessionId(1) {} + + static Mutex sSingletonLock; + static SessionLibrary* sSingleton; + + Mutex mSessionsLock; + uint32_t mNextSessionId; + std::map<std::vector<uint8_t>, sp<Session> > mSessions; +}; + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android + +#endif // CLEARKEY_SESSION_LIBRARY_H_ diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/TypeConvert.h b/drm/mediadrm/plugins/clearkey/hidl/include/TypeConvert.h new file mode 100644 index 0000000000..cc06329b84 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/include/TypeConvert.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CLEARKEY_ANDROID_HARDWARE_DRM_V1_1_TYPECONVERT +#define CLEARKEY_ANDROID_HARDWARE_DRM_V1_1_TYPECONVERT + +#include <vector> + +#include <android/hardware/drm/1.0/types.h> + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +using ::android::hardware::hidl_array; +using ::android::hardware::hidl_vec; + +template<typename T> const hidl_vec<T> toHidlVec(const std::vector<T> &vec) { + hidl_vec<T> hVec; + hVec.setToExternal(const_cast<T *>(vec.data()), vec.size()); + return hVec; +} + +template<typename T> hidl_vec<T> toHidlVec(std::vector<T> &vec) { + hidl_vec<T> hVec; + hVec.setToExternal(vec.data(), vec.size()); + return hVec; +} + +template<typename T> const std::vector<T> toVector(const hidl_vec<T> &hVec) { + std::vector<T> vec; + vec.assign(hVec.data(), hVec.data() + hVec.size()); + return *const_cast<const std::vector<T> *>(&vec); +} + +template<typename T> std::vector<T> toVector(hidl_vec<T> &hVec) { + std::vector<T> vec; + vec.assign(hVec.data(), hVec.data() + hVec.size()); + return vec; +} + +template<typename T, size_t SIZE> const std::vector<T> toVector( + const hidl_array<T, SIZE> &hArray) { + std::vector<T> vec; + vec.assign(hArray.data(), hArray.data() + hArray.size()); + return vec; +} + +template<typename T, size_t SIZE> std::vector<T> toVector( + hidl_array<T, SIZE> &hArray) { + std::vector<T> vec; + vec.assign(hArray.data(), hArray.data() + hArray.size()); + return vec; +} + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android + +#endif // CLEARKEY_ANDROID_HARDWARE_DRM_V1_1_TYPECONVERT diff --git a/drm/mediadrm/plugins/clearkey/hidl/service.cpp b/drm/mediadrm/plugins/clearkey/hidl/service.cpp new file mode 100644 index 0000000000..6a97b720d2 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/service.cpp @@ -0,0 +1,54 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define LOG_TAG "android.hardware.drm@1.1-service.clearkey" + +#include <CryptoFactory.h> +#include <DrmFactory.h> + +#include <android-base/logging.h> +#include <binder/ProcessState.h> +#include <hidl/HidlTransportSupport.h> + +using ::android::hardware::configureRpcThreadpool; +using ::android::hardware::joinRpcThreadpool; +using ::android::sp; + +using android::hardware::drm::V1_1::ICryptoFactory; +using android::hardware::drm::V1_1::IDrmFactory; +using android::hardware::drm::V1_1::clearkey::CryptoFactory; +using android::hardware::drm::V1_1::clearkey::DrmFactory; + + +int main(int /* argc */, char** /* argv */) { + ALOGD("android.hardware.drm@1.1-service.clearkey starting..."); + + // The DRM HAL may communicate to other vendor components via + // /dev/vndbinder + android::ProcessState::initWithDriver("/dev/vndbinder"); + + sp<IDrmFactory> drmFactory = new DrmFactory; + sp<ICryptoFactory> cryptoFactory = new CryptoFactory; + + configureRpcThreadpool(8, true /* callerWillJoin */); + + // Setup hwbinder service + CHECK_EQ(drmFactory->registerAsService("clearkey"), android::NO_ERROR) + << "Failed to register Clearkey Factory HAL"; + CHECK_EQ(cryptoFactory->registerAsService("clearkey"), android::NO_ERROR) + << "Failed to register Clearkey Crypto HAL"; + + joinRpcThreadpool(); +} |
