Use aidl interface for clearkey service
The interface is defined in hardware/interfaces/drm/aidl
(change 15329852).
Test: atest VtsAidlHalDrmTargetTest
Bug: 170964303
Bug: 200055138
Change-Id: If8670ffab6474700ee98262156cee62e7cf5dcf8
diff --git a/drm/mediadrm/plugins/clearkey/aidl/Android.bp b/drm/mediadrm/plugins/clearkey/aidl/Android.bp
new file mode 100644
index 0000000..2997b67
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/aidl/Android.bp
@@ -0,0 +1,72 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "hardware_interfaces_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_av_license"],
+}
+
+cc_defaults {
+ name: "aidl_clearkey_service_defaults",
+ vendor: true,
+
+ srcs: [
+ "CreatePluginFactories.cpp",
+ "CryptoFactory.cpp",
+ "CryptoPlugin.cpp",
+ "DrmFactory.cpp",
+ "DrmPlugin.cpp",
+ ],
+
+ relative_install_path: "hw",
+
+ cflags: ["-Wall", "-Werror", "-Wthread-safety"],
+
+ include_dirs: ["frameworks/av/include"],
+
+ shared_libs: [
+ "libbase",
+ "libbinder_ndk",
+ "libcrypto",
+ "liblog",
+ "libprotobuf-cpp-lite",
+ "libutils",
+ "android.hardware.drm-V1-ndk",
+ ],
+
+ static_libs: [
+ "android.hardware.common-V2-ndk",
+ "libclearkeybase",
+ ],
+
+ local_include_dirs: ["include"],
+
+ sanitize: {
+ integer_overflow: true,
+ },
+}
+
+cc_binary {
+ name: "android.hardware.drm-service.clearkey",
+ defaults: ["aidl_clearkey_service_defaults"],
+ srcs: ["Service.cpp"],
+ init_rc: ["android.hardware.drm-service.clearkey.rc"],
+ vintf_fragments: ["android.hardware.drm-service.clearkey.xml"],
+}
+
+cc_binary {
+ name: "android.hardware.drm-service-lazy.clearkey",
+ defaults: ["aidl_clearkey_service_defaults"],
+ overrides: ["android.hardware.drm-service.clearkey"],
+ srcs: ["ServiceLazy.cpp"],
+ init_rc: ["android.hardware.drm-service-lazy.clearkey.rc"],
+ vintf_fragments: ["android.hardware.drm-service.clearkey.xml"],
+}
+
+phony {
+ name: "android.hardware.drm@latest-service.clearkey",
+ required: [
+ "android.hardware.drm-service.clearkey",
+ ],
+}
diff --git a/drm/mediadrm/plugins/clearkey/aidl/CreatePluginFactories.cpp b/drm/mediadrm/plugins/clearkey/aidl/CreatePluginFactories.cpp
new file mode 100644
index 0000000..5f6bfe8
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/aidl/CreatePluginFactories.cpp
@@ -0,0 +1,36 @@
+/*
+ * 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"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace drm {
+namespace clearkey {
+
+std::shared_ptr<DrmFactory> createDrmFactory() {
+ return ::ndk::SharedRefBase::make<DrmFactory>();
+}
+
+std::shared_ptr<CryptoFactory> createCryptoFactory() {
+ return ::ndk::SharedRefBase::make<CryptoFactory>();
+}
+
+} // namespace clearkey
+} // namespace drm
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/drm/mediadrm/plugins/clearkey/aidl/CryptoFactory.cpp b/drm/mediadrm/plugins/clearkey/aidl/CryptoFactory.cpp
new file mode 100644
index 0000000..43b325d
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/aidl/CryptoFactory.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2021 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 "clearkey-CryptoFactory"
+#include <utils/Log.h>
+
+#include "CryptoFactory.h"
+
+#include "ClearKeyUUID.h"
+#include "CryptoPlugin.h"
+#include "AidlUtils.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace drm {
+namespace clearkey {
+
+using ::aidl::android::hardware::drm::Status;
+using ::aidl::android::hardware::drm::Uuid;
+
+using std::vector;
+
+::ndk::ScopedAStatus CryptoFactory::createPlugin(
+ const ::aidl::android::hardware::drm::Uuid& in_uuid,
+ const std::vector<uint8_t>& in_initData,
+ std::shared_ptr<::aidl::android::hardware::drm::ICryptoPlugin>* _aidl_return) {
+ if (!isClearKeyUUID(in_uuid.uuid.data())) {
+ ALOGE("Clearkey Drm HAL: failed to create crypto plugin, "
+ "invalid crypto scheme");
+ *_aidl_return = nullptr;
+ return toNdkScopedAStatus(Status::BAD_VALUE);
+ }
+
+ std::shared_ptr<CryptoPlugin> plugin = ::ndk::SharedRefBase::make<CryptoPlugin>(in_initData);
+ Status status = plugin->getInitStatus();
+ if (status != Status::OK) {
+ plugin.reset();
+ plugin = nullptr;
+ }
+ *_aidl_return = plugin;
+ return toNdkScopedAStatus(status);
+}
+
+::ndk::ScopedAStatus CryptoFactory::isCryptoSchemeSupported(const Uuid& in_uuid,
+ bool* _aidl_return) {
+ *_aidl_return = isClearKeyUUID(in_uuid.uuid.data());
+ return ::ndk::ScopedAStatus::ok();
+}
+
+} // namespace clearkey
+} // namespace drm
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/drm/mediadrm/plugins/clearkey/aidl/CryptoPlugin.cpp b/drm/mediadrm/plugins/clearkey/aidl/CryptoPlugin.cpp
new file mode 100644
index 0000000..b65d40f
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/aidl/CryptoPlugin.cpp
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2021 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 "clearkey-CryptoPlugin"
+
+#include <utils/Log.h>
+#include <cerrno>
+#include <cstring>
+
+#include "CryptoPlugin.h"
+#include "SessionLibrary.h"
+#include "AidlUtils.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace drm {
+namespace clearkey {
+
+using ::aidl::android::hardware::drm::Status;
+
+::ndk::ScopedAStatus CryptoPlugin::decrypt(
+ bool in_secure, const std::vector<uint8_t>& in_keyId, const std::vector<uint8_t>& in_iv,
+ ::aidl::android::hardware::drm::Mode in_mode,
+ const ::aidl::android::hardware::drm::Pattern& in_pattern,
+ const std::vector<::aidl::android::hardware::drm::SubSample>& in_subSamples,
+ const ::aidl::android::hardware::drm::SharedBuffer& in_source, int64_t in_offset,
+ const ::aidl::android::hardware::drm::DestinationBuffer& in_destination,
+ ::aidl::android::hardware::drm::DecryptResult* _aidl_return) {
+ UNUSED(in_pattern);
+
+ std::string detailedError;
+
+ _aidl_return->bytesWritten = 0;
+ if (in_secure) {
+ _aidl_return->detailedError = "secure decryption is not supported with ClearKey";
+ return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE);
+ }
+
+ std::lock_guard<std::mutex> shared_buffer_lock(mSharedBufferLock);
+ if (mSharedBufferMap.find(in_source.bufferId) == mSharedBufferMap.end()) {
+ _aidl_return->detailedError = "source decrypt buffer base not set";
+ return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE);
+ }
+
+ if (in_destination.type == BufferType::SHARED_MEMORY) {
+ const SharedBuffer& dest = in_destination.nonsecureMemory;
+ if (mSharedBufferMap.find(dest.bufferId) == mSharedBufferMap.end()) {
+ _aidl_return->detailedError = "destination decrypt buffer base not set";
+ return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE);
+ }
+ } else {
+ _aidl_return->detailedError = "destination type not supported";
+ return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE);
+ }
+
+ auto src = mSharedBufferMap[in_source.bufferId];
+ if (src->mBase == nullptr) {
+ _aidl_return->detailedError = "source is a nullptr";
+ return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE);
+ }
+
+ size_t totalSize = 0;
+ if (__builtin_add_overflow(in_source.offset, in_offset, &totalSize) ||
+ __builtin_add_overflow(totalSize, in_source.size, &totalSize) || totalSize > src->mSize) {
+ android_errorWriteLog(0x534e4554, "176496160");
+ _aidl_return->detailedError = "invalid buffer size";
+ return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE);
+ }
+
+ // destination.type == BufferType::SHARED_MEMORY
+ const SharedBuffer& destBuffer = in_destination.nonsecureMemory;
+ auto dest = mSharedBufferMap[destBuffer.bufferId];
+ if (dest->mBase == nullptr) {
+ _aidl_return->detailedError = "destination is a nullptr";
+ return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE);
+ }
+
+ totalSize = 0;
+ if (__builtin_add_overflow(destBuffer.offset, destBuffer.size, &totalSize) ||
+ totalSize > dest->mSize) {
+ android_errorWriteLog(0x534e4554, "176444622");
+ _aidl_return->detailedError = "invalid buffer size";
+ return toNdkScopedAStatus(Status::ERROR_DRM_FRAME_TOO_LARGE);
+ }
+
+ // Calculate the output buffer size and determine if any subsamples are
+ // encrypted.
+ uint8_t* srcPtr = src->mBase + in_source.offset + in_offset;
+ uint8_t* destPtr = dest->mBase + in_destination.nonsecureMemory.offset;
+ size_t destSize = 0;
+ size_t srcSize = 0;
+ bool haveEncryptedSubsamples = false;
+ for (size_t i = 0; i < in_subSamples.size(); i++) {
+ const SubSample& subSample = in_subSamples[i];
+ if (__builtin_add_overflow(destSize, subSample.numBytesOfClearData, &destSize) ||
+ __builtin_add_overflow(srcSize, subSample.numBytesOfClearData, &srcSize)) {
+ _aidl_return->detailedError = "subsample clear size overflow";
+ return toNdkScopedAStatus(Status::ERROR_DRM_FRAME_TOO_LARGE);
+ }
+ if (__builtin_add_overflow(destSize, subSample.numBytesOfEncryptedData, &destSize) ||
+ __builtin_add_overflow(srcSize, subSample.numBytesOfEncryptedData, &srcSize)) {
+ _aidl_return->detailedError = "subsample encrypted size overflow";
+ return toNdkScopedAStatus(Status::ERROR_DRM_FRAME_TOO_LARGE);
+ }
+ if (subSample.numBytesOfEncryptedData > 0) {
+ haveEncryptedSubsamples = true;
+ }
+ }
+
+ if (destSize > destBuffer.size || srcSize > in_source.size) {
+ _aidl_return->detailedError = "subsample sum too large";
+ return toNdkScopedAStatus(Status::ERROR_DRM_FRAME_TOO_LARGE);
+ }
+
+ if (in_mode == Mode::UNENCRYPTED) {
+ if (haveEncryptedSubsamples) {
+ _aidl_return->detailedError =
+ "Encrypted subsamples found in allegedly unencrypted data.";
+ return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE);
+ }
+
+ size_t offset = 0;
+ for (size_t i = 0; i < in_subSamples.size(); ++i) {
+ const SubSample& subSample = in_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;
+ }
+ }
+
+ _aidl_return->bytesWritten = static_cast<ssize_t>(offset);
+ _aidl_return->detailedError = "";
+ return toNdkScopedAStatus(Status::OK);
+ } else if (in_mode == Mode::AES_CTR) {
+ size_t bytesDecrypted{};
+ std::vector<int32_t> clearDataLengths;
+ std::vector<int32_t> encryptedDataLengths;
+ for (auto ss : in_subSamples) {
+ clearDataLengths.push_back(ss.numBytesOfClearData);
+ encryptedDataLengths.push_back(ss.numBytesOfEncryptedData);
+ }
+ auto res =
+ mSession->decrypt(in_keyId.data(), in_iv.data(),
+ srcPtr, static_cast<uint8_t*>(destPtr),
+ clearDataLengths, encryptedDataLengths,
+ &bytesDecrypted);
+ if (res == clearkeydrm::OK) {
+ _aidl_return->bytesWritten = static_cast<ssize_t>(bytesDecrypted);
+ _aidl_return->detailedError = "";
+ return toNdkScopedAStatus(Status::OK);
+ } else {
+ _aidl_return->bytesWritten = 0;
+ _aidl_return->detailedError = "Decryption Error";
+ return toNdkScopedAStatus(static_cast<Status>(res));
+ }
+ } else {
+ _aidl_return->bytesWritten = 0;
+ _aidl_return->detailedError =
+ "selected encryption mode is not supported by the ClearKey DRM \
+Plugin";
+ return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE);
+ }
+}
+
+::ndk::ScopedAStatus CryptoPlugin::getLogMessages(
+ std::vector<::aidl::android::hardware::drm::LogMessage>* _aidl_return) {
+ using std::chrono::duration_cast;
+ using std::chrono::milliseconds;
+ using std::chrono::system_clock;
+
+ auto timeMillis = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
+
+ std::vector<::aidl::android::hardware::drm::LogMessage> logs = {
+ {timeMillis, ::aidl::android::hardware::drm::LogPriority::ERROR,
+ std::string("Not implemented")}};
+ *_aidl_return = logs;
+ return toNdkScopedAStatus(Status::OK);
+}
+
+::ndk::ScopedAStatus CryptoPlugin::notifyResolution(int32_t in_width, int32_t in_height) {
+ UNUSED(in_width);
+ UNUSED(in_height);
+ return ::ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus CryptoPlugin::requiresSecureDecoderComponent(const std::string& in_mime,
+ bool* _aidl_return) {
+ UNUSED(in_mime);
+ *_aidl_return = false;
+ return ::ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus CryptoPlugin::setMediaDrmSession(const std::vector<uint8_t>& in_sessionId) {
+ Status status = Status::OK;
+ if (!in_sessionId.size()) {
+ mSession = nullptr;
+ } else {
+ mSession = SessionLibrary::get()->findSession(in_sessionId);
+ if (!mSession.get()) {
+ status = Status::ERROR_DRM_SESSION_NOT_OPENED;
+ }
+ }
+ return toNdkScopedAStatus(status);
+}
+
+::ndk::ScopedAStatus CryptoPlugin::setSharedBufferBase(
+ const ::aidl::android::hardware::common::Ashmem& in_base, int32_t in_bufferId) {
+ std::lock_guard<std::mutex> shared_buffer_lock(mSharedBufferLock);
+ mSharedBufferMap[in_bufferId] = std::make_shared<SharedBufferBase>(in_base);
+ return ::ndk::ScopedAStatus::ok();
+}
+
+SharedBufferBase::SharedBufferBase(const ::aidl::android::hardware::common::Ashmem& mem)
+ : mBase(nullptr),
+ mSize(mem.size) {
+ if (mem.fd.get() < 0) {
+ return;
+ }
+ auto addr = mmap(nullptr, mem.size, PROT_READ | PROT_WRITE, MAP_SHARED,
+ mem.fd.get(), 0);
+ if (addr == MAP_FAILED) {
+ ALOGE("mmap err: fd %d; errno %s",
+ mem.fd.get(), strerror(errno));
+ } else {
+ mBase = static_cast<uint8_t*>(addr);
+ }
+}
+
+SharedBufferBase::~SharedBufferBase() {
+ if (munmap(mBase, mSize)) {
+ ALOGE("munmap err: base %p; errno %s",
+ mBase, strerror(errno));
+ }
+}
+} // namespace clearkey
+} // namespace drm
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/drm/mediadrm/plugins/clearkey/aidl/DrmFactory.cpp b/drm/mediadrm/plugins/clearkey/aidl/DrmFactory.cpp
new file mode 100644
index 0000000..168a661
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/aidl/DrmFactory.cpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2021 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 "clearkey-DrmFactory"
+
+#include <utils/Log.h>
+
+#include "DrmFactory.h"
+
+#include "ClearKeyUUID.h"
+#include "DrmPlugin.h"
+#include "MimeTypeStdStr.h"
+#include "SessionLibrary.h"
+#include "AidlUtils.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace drm {
+namespace clearkey {
+
+using std::string;
+using std::vector;
+
+using ::aidl::android::hardware::drm::SecurityLevel;
+using ::aidl::android::hardware::drm::Status;
+using ::aidl::android::hardware::drm::Uuid;
+
+::ndk::ScopedAStatus DrmFactory::createPlugin(
+ const Uuid& in_uuid, const string& in_appPackageName,
+ std::shared_ptr<::aidl::android::hardware::drm::IDrmPlugin>* _aidl_return) {
+ UNUSED(in_appPackageName);
+
+ if (!isClearKeyUUID(in_uuid.uuid.data())) {
+ ALOGE("Clearkey Drm HAL: failed to create drm plugin, "
+ "invalid crypto scheme");
+ *_aidl_return = nullptr;
+ return toNdkScopedAStatus(Status::BAD_VALUE);
+ }
+
+ std::shared_ptr<DrmPlugin> plugin =
+ ::ndk::SharedRefBase::make<DrmPlugin>(SessionLibrary::get());
+ *_aidl_return = plugin;
+ return toNdkScopedAStatus(Status::OK);
+}
+
+::ndk::ScopedAStatus DrmFactory::getSupportedCryptoSchemes(vector<Uuid>* _aidl_return) {
+ vector<Uuid> schemes;
+ Uuid scheme;
+ for (const auto& uuid : ::aidl::android::hardware::drm::clearkey::getSupportedCryptoSchemes()) {
+ scheme.uuid.assign(uuid.begin(), uuid.end());
+ schemes.push_back(scheme);
+ }
+ *_aidl_return = schemes;
+ return ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus DrmFactory::isContentTypeSupported(const string& in_mimeType,
+ bool* _aidl_return) {
+ // This should match the in_mimeTypes handed by InitDataParser.
+ *_aidl_return = in_mimeType == kIsoBmffVideoMimeType || in_mimeType == kIsoBmffAudioMimeType ||
+ in_mimeType == kCencInitDataFormat || in_mimeType == kWebmVideoMimeType ||
+ in_mimeType == kWebmAudioMimeType || in_mimeType == kWebmInitDataFormat;
+ return ::ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus DrmFactory::isCryptoSchemeSupported(const Uuid& in_uuid,
+ const string& in_mimeType,
+ SecurityLevel in_securityLevel,
+ bool* _aidl_return) {
+ bool isSupportedMimeType = false;
+ if (!isContentTypeSupported(in_mimeType, &isSupportedMimeType).isOk()) {
+ ALOGD("%s mime type is not supported by crypto scheme", in_mimeType.c_str());
+ }
+ *_aidl_return = isClearKeyUUID(in_uuid.uuid.data()) && isSupportedMimeType &&
+ in_securityLevel == SecurityLevel::SW_SECURE_CRYPTO;
+ return ::ndk::ScopedAStatus::ok();
+}
+
+binder_status_t DrmFactory::dump(int fd, const char** args, uint32_t numArgs) {
+ UNUSED(args);
+ UNUSED(numArgs);
+
+ if (fd < 0) {
+ ALOGE("%s: negative fd", __FUNCTION__);
+ return STATUS_BAD_VALUE;
+ }
+
+ uint32_t currentSessions = SessionLibrary::get()->numOpenSessions();
+ dprintf(fd, "current open sessions: %u\n", currentSessions);
+
+ return STATUS_OK;
+}
+
+} // namespace clearkey
+} // namespace drm
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/drm/mediadrm/plugins/clearkey/aidl/DrmPlugin.cpp b/drm/mediadrm/plugins/clearkey/aidl/DrmPlugin.cpp
new file mode 100644
index 0000000..e9301b2
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/aidl/DrmPlugin.cpp
@@ -0,0 +1,1039 @@
+/*
+ * Copyright (C) 2021 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 "clearkey-DrmPlugin"
+
+#include <aidl/android/hardware/drm/DrmMetric.h>
+#include <utils/Log.h>
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <chrono>
+
+#include "AidlUtils.h"
+#include "ClearKeyDrmProperties.h"
+#include "DrmPlugin.h"
+#include "Session.h"
+#include "Utils.h"
+
+namespace {
+const std::string kKeySetIdPrefix("ckid");
+const int kKeySetIdLength = 16;
+const int kSecureStopIdStart = 100;
+const std::string kOfflineLicense("\"type\":\"persistent-license\"");
+const std::string kStreaming("Streaming");
+const std::string kTemporaryLicense("\"type\":\"temporary\"");
+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"
+
+const int kSecureStopIdSize = 10;
+
+std::vector<uint8_t> uint32ToVector(uint32_t value) {
+ // 10 bytes to display max value 4294967295 + one byte null terminator
+ char buffer[kSecureStopIdSize];
+ memset(buffer, 0, kSecureStopIdSize);
+ snprintf(buffer, kSecureStopIdSize, "%" PRIu32, value);
+ return std::vector<uint8_t>(buffer, buffer + sizeof(buffer));
+}
+
+}; // unnamed namespace
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace drm {
+namespace clearkey {
+
+using ::android::Mutex;
+
+DrmPlugin::DrmPlugin(SessionLibrary* sessionLibrary)
+ : mSessionLibrary(sessionLibrary),
+ mOpenSessionOkCount(0),
+ mCloseSessionOkCount(0),
+ mCloseSessionNotOpenedCount(0),
+ mNextSecureStopId(kSecureStopIdStart),
+ mMockError(Status::OK) {
+ mPlayPolicy.clear();
+ initProperties();
+ mSecureStops.clear();
+ mReleaseKeysMap.clear();
+ std::srand(std::time(nullptr));
+}
+
+void DrmPlugin::initProperties() {
+ mStringProperties.clear();
+ mStringProperties[kVendorKey] = kVendorValue;
+ mStringProperties[kVersionKey] = kVersionValue;
+ mStringProperties[kPluginDescriptionKey] = kPluginDescriptionValue;
+ mStringProperties[kAlgorithmsKey] = kAlgorithmsValue;
+ mStringProperties[kListenerTestSupportKey] = kListenerTestSupportValue;
+ mStringProperties[kDrmErrorTestKey] = kDrmErrorTestValue;
+
+ 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;
+}
+
+// The secure stop in ClearKey implementation is not installed securely.
+// This function merely creates a test environment for testing secure stops APIs.
+// The content in this secure stop is implementation dependent, the clearkey
+// secureStop does not serve as a reference implementation.
+void DrmPlugin::installSecureStop(const std::vector<uint8_t>& sessionId) {
+ ::android::Mutex::Autolock lock(mSecureStopLock);
+
+ ClearkeySecureStop clearkeySecureStop;
+ clearkeySecureStop.id = uint32ToVector(++mNextSecureStopId);
+ clearkeySecureStop.data.assign(sessionId.begin(), sessionId.end());
+
+ mSecureStops.insert(std::pair<std::vector<uint8_t>, ClearkeySecureStop>(clearkeySecureStop.id,
+ clearkeySecureStop));
+}
+
+::ndk::ScopedAStatus DrmPlugin::closeSession(const std::vector<uint8_t>& in_sessionId) {
+ if (in_sessionId.size() == 0) {
+ return toNdkScopedAStatus(Status::BAD_VALUE);
+ }
+
+ ::android::sp<Session> session = mSessionLibrary->findSession(in_sessionId);
+ if (session.get()) {
+ mSessionLibrary->destroySession(session);
+ if (session->getMockError() != clearkeydrm::OK) {
+ sendSessionLostState(in_sessionId);
+ return toNdkScopedAStatus(Status::ERROR_DRM_INVALID_STATE);
+ }
+ mCloseSessionOkCount++;
+ return toNdkScopedAStatus(Status::OK);
+ }
+ mCloseSessionNotOpenedCount++;
+ return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED);
+}
+
+::ndk::ScopedAStatus DrmPlugin::decrypt(const std::vector<uint8_t>& in_sessionId,
+ const std::vector<uint8_t>& in_keyId,
+ const std::vector<uint8_t>& in_input,
+ const std::vector<uint8_t>& in_iv,
+ std::vector<uint8_t>* _aidl_return) {
+ *_aidl_return = {};
+ if (in_sessionId.size() == 0 || in_keyId.size() == 0 || in_input.size() == 0 ||
+ in_iv.size() == 0) {
+ return toNdkScopedAStatus(Status::BAD_VALUE);
+ }
+ return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE);
+}
+
+::ndk::ScopedAStatus DrmPlugin::encrypt(const std::vector<uint8_t>& in_sessionId,
+ const std::vector<uint8_t>& in_keyId,
+ const std::vector<uint8_t>& in_input,
+ const std::vector<uint8_t>& in_iv,
+ std::vector<uint8_t>* _aidl_return) {
+ *_aidl_return = {};
+ if (in_sessionId.size() == 0 || in_keyId.size() == 0 || in_input.size() == 0 ||
+ in_iv.size() == 0) {
+ return toNdkScopedAStatus(Status::BAD_VALUE);
+ }
+ return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE);
+}
+
+::ndk::ScopedAStatus DrmPlugin::getHdcpLevels(
+ ::aidl::android::hardware::drm::HdcpLevels* _aidl_return) {
+ _aidl_return->connectedLevel = HdcpLevel::HDCP_NONE;
+ _aidl_return->maxLevel = HdcpLevel::HDCP_NO_OUTPUT;
+ return toNdkScopedAStatus(Status::OK);
+}
+
+::ndk::ScopedAStatus DrmPlugin::getKeyRequest(
+ const std::vector<uint8_t>& in_scope, const std::vector<uint8_t>& in_initData,
+ const std::string& in_mimeType, ::aidl::android::hardware::drm::KeyType in_keyType,
+ const std::vector<::aidl::android::hardware::drm::KeyValue>& in_optionalParameters,
+ ::aidl::android::hardware::drm::KeyRequest* _aidl_return) {
+ UNUSED(in_optionalParameters);
+
+ KeyRequestType keyRequestType = KeyRequestType::UNKNOWN;
+ std::string defaultUrl("");
+
+ _aidl_return->request = {};
+ _aidl_return->requestType = keyRequestType;
+ _aidl_return->defaultUrl = defaultUrl;
+
+ if (in_scope.size() == 0 ||
+ (in_keyType != KeyType::STREAMING && in_keyType != KeyType::OFFLINE &&
+ in_keyType != KeyType::RELEASE)) {
+ return toNdkScopedAStatus(Status::BAD_VALUE);
+ }
+
+ const std::vector<uint8_t> scopeId = in_scope;
+ ::android::sp<Session> session;
+ if (in_keyType == KeyType::STREAMING || in_keyType == KeyType::OFFLINE) {
+ std::vector<uint8_t> sessionId(scopeId.begin(), scopeId.end());
+ session = mSessionLibrary->findSession(sessionId);
+ if (!session.get()) {
+ return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED);
+ } else if (session->getMockError() != clearkeydrm::OK) {
+ return toNdkScopedAStatus(session->getMockError());
+ }
+ keyRequestType = KeyRequestType::INITIAL;
+ }
+
+ std::vector<uint8_t> request = {};
+ auto keyType = static_cast<CdmKeyType>(in_keyType);
+ auto status = session->getKeyRequest(in_initData, in_mimeType, keyType, &request);
+
+ if (in_keyType == KeyType::RELEASE) {
+ std::vector<uint8_t> keySetId(scopeId.begin(), scopeId.end());
+ std::string requestString(request.begin(), request.end());
+ if (requestString.find(kOfflineLicense) != std::string::npos) {
+ std::string emptyResponse;
+ std::string keySetIdString(keySetId.begin(), keySetId.end());
+ if (!mFileHandle.StoreLicense(keySetIdString, DeviceFiles::kLicenseStateReleasing,
+ emptyResponse)) {
+ ALOGE("Problem releasing offline license");
+ return toNdkScopedAStatus(Status::ERROR_DRM_UNKNOWN);
+ }
+ if (mReleaseKeysMap.find(keySetIdString) == mReleaseKeysMap.end()) {
+ ::android::sp<Session> session = mSessionLibrary->createSession();
+ mReleaseKeysMap[keySetIdString] = session->sessionId();
+ } else {
+ ALOGI("key is in use, ignore release request");
+ }
+ } else {
+ ALOGE("Offline license not found, nothing to release");
+ }
+ keyRequestType = KeyRequestType::RELEASE;
+ }
+ _aidl_return->request = request;
+ _aidl_return->requestType = keyRequestType;
+ _aidl_return->defaultUrl = defaultUrl;
+ return toNdkScopedAStatus(status);
+}
+
+::ndk::ScopedAStatus DrmPlugin::getLogMessages(
+ std::vector<::aidl::android::hardware::drm::LogMessage>* _aidl_return) {
+ using std::chrono::duration_cast;
+ using std::chrono::milliseconds;
+ using std::chrono::system_clock;
+
+ auto timeMillis = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
+
+ std::vector<::aidl::android::hardware::drm::LogMessage> logs = {
+ {timeMillis, ::aidl::android::hardware::drm::LogPriority::ERROR,
+ std::string("Not implemented")}};
+ *_aidl_return = logs;
+ return toNdkScopedAStatus(Status::OK);
+}
+
+::ndk::ScopedAStatus DrmPlugin::getMetrics(
+ std::vector<::aidl::android::hardware::drm::DrmMetricGroup>* _aidl_return) {
+ // Set the open session count metric.
+ DrmMetricNamedValue openSessionOkAttribute = {"status", static_cast<int64_t>(Status::OK)};
+ DrmMetricNamedValue openSessionMetricValue = {"count", mOpenSessionOkCount};
+ DrmMetric openSessionMetric = {
+ "open_session", {openSessionOkAttribute}, {openSessionMetricValue}};
+
+ // Set the close session count metric.
+ DrmMetricNamedValue closeSessionOkAttribute = {"status", static_cast<int64_t>(Status::OK)};
+ DrmMetricNamedValue closeSessionMetricValue = {"count", mCloseSessionOkCount};
+ DrmMetric closeSessionMetric = {
+ "close_session", {closeSessionOkAttribute}, {closeSessionMetricValue}};
+
+ // Set the close session, not opened metric.
+ DrmMetricNamedValue closeSessionNotOpenedAttribute = {"status",
+ static_cast<int64_t>(Status::ERROR_DRM_SESSION_NOT_OPENED)};
+ DrmMetricNamedValue closeSessionNotOpenedMetricValue = {"count", mCloseSessionNotOpenedCount};
+ DrmMetric closeSessionNotOpenedMetric = {
+ "close_session", {closeSessionNotOpenedAttribute}, {closeSessionNotOpenedMetricValue}};
+
+ // Set the setPlaybackId metric.
+ std::vector<DrmMetricNamedValue> sids = {};
+ std::vector<DrmMetricNamedValue> playbackIds = {};
+ for (const auto& [key, value] : mPlaybackId) {
+ std::string sid(key.begin(), key.end());
+ DrmMetricNamedValue sessionIdAttribute = {"sid", sid};
+ sids.push_back(sessionIdAttribute);
+
+ DrmMetricNamedValue playbackIdMetricValue = {"playbackId", value};
+ playbackIds.push_back(playbackIdMetricValue);
+ }
+ DrmMetric setPlaybackIdMetric = {"set_playback_id", sids, playbackIds};
+
+ DrmMetricGroup metrics = {{openSessionMetric, closeSessionMetric, closeSessionNotOpenedMetric,
+ setPlaybackIdMetric}};
+
+ *_aidl_return = {metrics};
+ return toNdkScopedAStatus(Status::OK);
+}
+
+::ndk::ScopedAStatus DrmPlugin::getNumberOfSessions(
+ ::aidl::android::hardware::drm::NumberOfSessions* _aidl_return) {
+ _aidl_return->currentSessions = mSessionLibrary->numOpenSessions();
+ _aidl_return->maxSessions = 10;
+ return toNdkScopedAStatus(Status::OK);
+}
+
+::ndk::ScopedAStatus DrmPlugin::getOfflineLicenseKeySetIds(
+ std::vector<::aidl::android::hardware::drm::KeySetId>* _aidl_return) {
+ std::vector<std::string> licenseNames = mFileHandle.ListLicenses();
+ std::vector<KeySetId> keySetIds = {};
+ if (mMockError != Status::OK) {
+ *_aidl_return = keySetIds;
+ return toNdkScopedAStatus(toMockStatus(mMockError));
+ }
+ for (const auto& name : licenseNames) {
+ std::vector<uint8_t> keySetId(name.begin(), name.end());
+ KeySetId id = {};
+ id.keySetId = keySetId;
+ keySetIds.push_back(id);
+ }
+ *_aidl_return = keySetIds;
+ return toNdkScopedAStatus(Status::OK);
+}
+
+::ndk::ScopedAStatus DrmPlugin::getOfflineLicenseState(
+ const ::aidl::android::hardware::drm::KeySetId& in_keySetId,
+ ::aidl::android::hardware::drm::OfflineLicenseState* _aidl_return) {
+ std::string licenseName(in_keySetId.keySetId.begin(), in_keySetId.keySetId.end());
+ DeviceFiles::LicenseState state;
+ std::string license;
+ OfflineLicenseState licenseState = OfflineLicenseState::UNKNOWN;
+ Status status = Status::OK;
+ if (mMockError != Status::OK) {
+ *_aidl_return = licenseState;
+ return toNdkScopedAStatus(toMockStatus(mMockError));
+ } else if (mFileHandle.RetrieveLicense(licenseName, &state, &license)) {
+ switch (state) {
+ case DeviceFiles::kLicenseStateActive:
+ licenseState = OfflineLicenseState::USABLE;
+ break;
+ case DeviceFiles::kLicenseStateReleasing:
+ licenseState = OfflineLicenseState::INACTIVE;
+ break;
+ case DeviceFiles::kLicenseStateUnknown:
+ licenseState = OfflineLicenseState::UNKNOWN;
+ break;
+ }
+ } else {
+ status = Status::BAD_VALUE;
+ }
+ *_aidl_return = licenseState;
+ return toNdkScopedAStatus(status);
+}
+
+::ndk::ScopedAStatus DrmPlugin::getPropertyByteArray(const std::string& in_propertyName,
+ std::vector<uint8_t>* _aidl_return) {
+ std::map<std::string, std::vector<uint8_t>>::iterator itr =
+ mByteArrayProperties.find(std::string(in_propertyName.c_str()));
+ Status status = Status::OK;
+ if (itr != mByteArrayProperties.end()) {
+ *_aidl_return = itr->second;
+ } else {
+ ALOGE("App requested unknown property: %s", in_propertyName.c_str());
+ status = Status::BAD_VALUE;
+ *_aidl_return = {};
+ }
+ return toNdkScopedAStatus(status);
+}
+
+::ndk::ScopedAStatus DrmPlugin::getPropertyString(const std::string& in_propertyName,
+ std::string* _aidl_return) {
+ std::string name(in_propertyName.c_str());
+ std::string value;
+ Status status = Status::OK;
+
+ if (name == kVendorKey) {
+ value = mStringProperties[kVendorKey];
+ } else if (name == kVersionKey) {
+ value = mStringProperties[kVersionKey];
+ } else if (name == kPluginDescriptionKey) {
+ value = mStringProperties[kPluginDescriptionKey];
+ } else if (name == kAlgorithmsKey) {
+ value = mStringProperties[kAlgorithmsKey];
+ } else if (name == kListenerTestSupportKey) {
+ value = mStringProperties[kListenerTestSupportKey];
+ } else if (name == kDrmErrorTestKey) {
+ value = mStringProperties[kDrmErrorTestKey];
+ } else {
+ ALOGE("App requested unknown string property %s", name.c_str());
+ status = Status::ERROR_DRM_CANNOT_HANDLE;
+ }
+ *_aidl_return = value;
+ return toNdkScopedAStatus(status);
+}
+
+::ndk::ScopedAStatus DrmPlugin::getProvisionRequest(
+ const std::string& in_certificateType, const std::string& in_certificateAuthority,
+ ::aidl::android::hardware::drm::ProvisionRequest* _aidl_return) {
+ UNUSED(in_certificateType);
+ UNUSED(in_certificateAuthority);
+ _aidl_return->request = {};
+ _aidl_return->defaultUrl = {};
+ return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE);
+}
+
+::ndk::ScopedAStatus DrmPlugin::getSecureStop(
+ const ::aidl::android::hardware::drm::SecureStopId& in_secureStopId,
+ ::aidl::android::hardware::drm::SecureStop* _aidl_return) {
+ std::vector<uint8_t> stop = {};
+
+ mSecureStopLock.lock();
+ auto itr = mSecureStops.find(in_secureStopId.secureStopId);
+ if (itr != mSecureStops.end()) {
+ ClearkeySecureStop clearkeyStop = itr->second;
+ stop.assign(clearkeyStop.id.begin(), clearkeyStop.id.end());
+ stop.assign(clearkeyStop.data.begin(), clearkeyStop.data.end());
+ }
+ mSecureStopLock.unlock();
+
+ SecureStop secureStop = {};
+ Status status = Status::OK;
+ if (!stop.empty()) {
+ secureStop.opaqueData = stop;
+ } else {
+ status = Status::BAD_VALUE;
+ }
+ *_aidl_return = secureStop;
+ return toNdkScopedAStatus(status);
+}
+
+::ndk::ScopedAStatus DrmPlugin::getSecureStopIds(
+ std::vector<::aidl::android::hardware::drm::SecureStopId>* _aidl_return) {
+ mSecureStopLock.lock();
+ std::vector<::aidl::android::hardware::drm::SecureStopId> ids;
+ for (auto itr = mSecureStops.begin(); itr != mSecureStops.end(); ++itr) {
+ SecureStopId id;
+ id.secureStopId = itr->first;
+ ids.push_back(id);
+ }
+ mSecureStopLock.unlock();
+
+ *_aidl_return = ids;
+ return toNdkScopedAStatus(Status::OK);
+}
+
+::ndk::ScopedAStatus DrmPlugin::getSecureStops(
+ std::vector<::aidl::android::hardware::drm::SecureStop>* _aidl_return) {
+ mSecureStopLock.lock();
+ std::vector<::aidl::android::hardware::drm::SecureStop> stops;
+ for (auto itr = mSecureStops.begin(); itr != mSecureStops.end(); ++itr) {
+ ClearkeySecureStop clearkeyStop = itr->second;
+ std::vector<uint8_t> stop = {};
+ stop.assign(clearkeyStop.id.begin(), clearkeyStop.id.end());
+ stop.assign(clearkeyStop.data.begin(), clearkeyStop.data.end());
+
+ SecureStop secureStop;
+ secureStop.opaqueData = stop;
+ stops.push_back(secureStop);
+ }
+ mSecureStopLock.unlock();
+
+ *_aidl_return = stops;
+ return toNdkScopedAStatus(Status::OK);
+}
+
+::ndk::ScopedAStatus DrmPlugin::getSecurityLevel(
+ const std::vector<uint8_t>& in_sessionId,
+ ::aidl::android::hardware::drm::SecurityLevel* _aidl_return) {
+ if (in_sessionId.size() == 0) {
+ *_aidl_return = ::aidl::android::hardware::drm::SecurityLevel::UNKNOWN;
+ return toNdkScopedAStatus(Status::BAD_VALUE);
+ }
+
+ std::vector<uint8_t> sid = in_sessionId;
+ ::android::sp<Session> session = mSessionLibrary->findSession(sid);
+ if (!session.get()) {
+ *_aidl_return = SecurityLevel::UNKNOWN;
+ return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED);
+ }
+
+ std::map<std::vector<uint8_t>, ::aidl::android::hardware::drm::SecurityLevel>::iterator itr =
+ mSecurityLevel.find(sid);
+ if (itr == mSecurityLevel.end()) {
+ ALOGE("Session id not found");
+ *_aidl_return = SecurityLevel::UNKNOWN;
+ return toNdkScopedAStatus(Status::ERROR_DRM_INVALID_STATE);
+ }
+
+ *_aidl_return = itr->second;
+ return toNdkScopedAStatus(Status::OK);
+}
+
+::ndk::ScopedAStatus DrmPlugin::openSession(
+ ::aidl::android::hardware::drm::SecurityLevel in_securityLevel,
+ std::vector<uint8_t>* _aidl_return) {
+ ::android::sp<Session> session = mSessionLibrary->createSession();
+ processMockError(session);
+ std::vector<uint8_t> sessionId = session->sessionId();
+
+ Status status = setSecurityLevel(sessionId, in_securityLevel);
+ if (status == Status::OK) {
+ mOpenSessionOkCount++;
+ } else {
+ mSessionLibrary->destroySession(session);
+ sessionId.clear();
+ }
+ *_aidl_return = sessionId;
+ return toNdkScopedAStatus(status);
+}
+
+::ndk::ScopedAStatus DrmPlugin::provideKeyResponse(
+ const std::vector<uint8_t>& in_scope, const std::vector<uint8_t>& in_response,
+ ::aidl::android::hardware::drm::KeySetId* _aidl_return) {
+ if (in_scope.size() == 0 || in_response.size() == 0) {
+ // Returns empty keySetId
+ *_aidl_return = {};
+ return toNdkScopedAStatus(Status::BAD_VALUE);
+ }
+
+ std::string responseString(reinterpret_cast<const char*>(in_response.data()),
+ in_response.size());
+ const std::vector<uint8_t> scopeId = in_scope;
+ std::vector<uint8_t> sessionId = {};
+ std::string keySetId;
+
+ bool isOfflineLicense = responseString.find(kOfflineLicense) != std::string::npos;
+ if (scopeId.size() < kKeySetIdPrefix.size()) {
+ android_errorWriteLog(0x534e4554, "144507096");
+ *_aidl_return = {};
+ return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE);
+ }
+ bool isRelease = (memcmp(scopeId.data(), kKeySetIdPrefix.data(), kKeySetIdPrefix.size()) == 0);
+ if (isRelease) {
+ keySetId.assign(scopeId.begin(), scopeId.end());
+
+ auto iter = mReleaseKeysMap.find(std::string(keySetId.begin(), keySetId.end()));
+ if (iter != mReleaseKeysMap.end()) {
+ sessionId.assign(iter->second.begin(), iter->second.end());
+ }
+ } else {
+ sessionId.assign(scopeId.begin(), scopeId.end());
+ // non offline license returns empty keySetId
+ keySetId.clear();
+ }
+
+ ::android::sp<Session> session = mSessionLibrary->findSession(sessionId);
+ if (!session.get()) {
+ *_aidl_return = {};
+ return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED);
+ }
+ setPlayPolicy();
+
+ auto res = session->provideKeyResponse(in_response);
+ if (res == clearkeydrm::OK) {
+ if (isOfflineLicense) {
+ if (isRelease) {
+ mFileHandle.DeleteLicense(keySetId);
+ mSessionLibrary->destroySession(session);
+ } else {
+ if (!makeKeySetId(&keySetId)) {
+ *_aidl_return = {};
+ return toNdkScopedAStatus(Status::ERROR_DRM_UNKNOWN);
+ }
+
+ bool ok = mFileHandle.StoreLicense(
+ keySetId, DeviceFiles::kLicenseStateActive,
+ std::string(in_response.begin(), in_response.end()));
+ if (!ok) {
+ ALOGE("Failed to store offline license");
+ }
+ }
+ }
+
+ // Test calling AMediaDrm listeners.
+ sendEvent(EventType::VENDOR_DEFINED, sessionId, sessionId);
+
+ sendExpirationUpdate(sessionId, 100);
+
+ std::vector<KeyStatus> keysStatus = {};
+ KeyStatus keyStatus;
+
+ std::vector<uint8_t> keyId1 = {0xA, 0xB, 0xC};
+ keyStatus.keyId = keyId1;
+ keyStatus.type = KeyStatusType::USABLE;
+ keysStatus.push_back(keyStatus);
+
+ std::vector<uint8_t> keyId2 = {0xD, 0xE, 0xF};
+ keyStatus.keyId = keyId2;
+ keyStatus.type = KeyStatusType::EXPIRED;
+ keysStatus.push_back(keyStatus);
+
+ std::vector<uint8_t> keyId3 = {0x0, 0x1, 0x2};
+ keyStatus.keyId = keyId3;
+ keyStatus.type = KeyStatusType::USABLEINFUTURE;
+ keysStatus.push_back(keyStatus);
+
+ sendKeysChange(sessionId, keysStatus, true);
+
+ installSecureStop(sessionId);
+ } else {
+ ALOGE("provideKeyResponse returns error=%d", res);
+ }
+
+ std::vector<uint8_t> keySetIdVec(keySetId.begin(), keySetId.end());
+ _aidl_return->keySetId = keySetIdVec;
+ return toNdkScopedAStatus(res);
+}
+
+::ndk::ScopedAStatus DrmPlugin::provideProvisionResponse(
+ const std::vector<uint8_t>& in_response,
+ ::aidl::android::hardware::drm::ProvideProvisionResponseResult* _aidl_return) {
+ Status status = Status::ERROR_DRM_CANNOT_HANDLE;
+ _aidl_return->certificate = {};
+ _aidl_return->wrappedKey = {};
+ if (in_response.size() == 0) {
+ status = Status::BAD_VALUE;
+ }
+ return toNdkScopedAStatus(status);
+}
+
+::ndk::ScopedAStatus DrmPlugin::queryKeyStatus(
+ const std::vector<uint8_t>& in_sessionId,
+ std::vector<::aidl::android::hardware::drm::KeyValue>* _aidl_return) {
+ if (in_sessionId.size() == 0) {
+ // Returns empty key status KeyValue pair
+ *_aidl_return = {};
+ return toNdkScopedAStatus(Status::BAD_VALUE);
+ }
+
+ std::vector<KeyValue> infoMap = {};
+ mPlayPolicyLock.lock();
+ KeyValue keyValuePair;
+ for (size_t i = 0; i < mPlayPolicy.size(); ++i) {
+ keyValuePair.key = mPlayPolicy[i].key;
+ keyValuePair.value = mPlayPolicy[i].value;
+ infoMap.push_back(keyValuePair);
+ }
+ mPlayPolicyLock.unlock();
+ *_aidl_return = infoMap;
+ return toNdkScopedAStatus(Status::OK);
+}
+
+::ndk::ScopedAStatus DrmPlugin::releaseAllSecureStops() {
+ Status status = Status::OK;
+ const auto res = removeAllSecureStops();
+ if (!res.isOk() && res.getExceptionCode() == EX_SERVICE_SPECIFIC) {
+ status = static_cast<Status>(res.getServiceSpecificError());
+ }
+ return toNdkScopedAStatus(status);
+}
+
+::ndk::ScopedAStatus DrmPlugin::releaseSecureStop(
+ const ::aidl::android::hardware::drm::SecureStopId& in_secureStopId) {
+ Status status = Status::OK;
+ const auto res = removeSecureStop(in_secureStopId);
+ if (!res.isOk() && res.getExceptionCode() == EX_SERVICE_SPECIFIC) {
+ status = static_cast<Status>(res.getServiceSpecificError());
+ }
+ return toNdkScopedAStatus(status);
+}
+
+::ndk::ScopedAStatus DrmPlugin::releaseSecureStops(
+ const ::aidl::android::hardware::drm::OpaqueData& in_ssRelease) {
+ // OpaqueData starts with 4 byte decimal integer string
+ const size_t kFourBytesOffset = 4;
+ if (in_ssRelease.opaqueData.size() < kFourBytesOffset) {
+ ALOGE("Invalid secureStopRelease length");
+ return toNdkScopedAStatus(Status::BAD_VALUE);
+ }
+
+ Status status = Status::OK;
+ std::vector<uint8_t> input = in_ssRelease.opaqueData;
+
+ if (input.size() < kSecureStopIdSize + kFourBytesOffset) {
+ // The minimum size of secure stop has to contain
+ // a 4 bytes count and one secureStop id
+ ALOGE("Total size of secureStops is too short");
+ return toNdkScopedAStatus(Status::BAD_VALUE);
+ }
+
+ // The format of opaqueData is shared between the server
+ // and the drm service. The clearkey implementation consists of:
+ // count - number of secure stops
+ // list of fixed length secure stops
+ size_t countBufferSize = sizeof(uint32_t);
+ if (input.size() < countBufferSize) {
+ // SafetyNet logging
+ android_errorWriteLog(0x534e4554, "144766455");
+ return toNdkScopedAStatus(Status::BAD_VALUE);
+ }
+ uint32_t count = 0;
+ sscanf(reinterpret_cast<char*>(input.data()), "%04" PRIu32, &count);
+
+ // Avoid divide by 0 below.
+ if (count == 0) {
+ ALOGE("Invalid 0 secureStop count");
+ return toNdkScopedAStatus(Status::BAD_VALUE);
+ }
+
+ // Computes the fixed length secureStop size
+ size_t secureStopSize = (input.size() - kFourBytesOffset) / count;
+ if (secureStopSize < kSecureStopIdSize) {
+ // A valid secureStop contains the id plus data
+ ALOGE("Invalid secureStop size");
+ return toNdkScopedAStatus(Status::BAD_VALUE);
+ }
+ uint8_t* buffer = new uint8_t[secureStopSize];
+ size_t offset = kFourBytesOffset; // skip the count
+ for (size_t i = 0; i < count; ++i, offset += secureStopSize) {
+ memcpy(buffer, input.data() + offset, secureStopSize);
+
+ // A secureStop contains id+data, we only use the id for removal
+ std::vector<uint8_t> id(buffer, buffer + kSecureStopIdSize);
+ ::aidl::android::hardware::drm::SecureStopId secureStopId{id};
+ const auto res = removeSecureStop(secureStopId);
+ if (!res.isOk() && res.getExceptionCode() == EX_SERVICE_SPECIFIC) {
+ status = static_cast<Status>(res.getServiceSpecificError());
+ }
+ return toNdkScopedAStatus(status);
+ if (Status::OK != status) break;
+ }
+
+ delete[] buffer;
+ return toNdkScopedAStatus(status);
+}
+
+::ndk::ScopedAStatus DrmPlugin::removeAllSecureStops() {
+ Mutex::Autolock lock(mSecureStopLock);
+
+ mSecureStops.clear();
+ mNextSecureStopId = kSecureStopIdStart;
+ return toNdkScopedAStatus(Status::OK);
+}
+
+::ndk::ScopedAStatus DrmPlugin::removeKeys(const std::vector<uint8_t>& in_sessionId) {
+ Status status = Status::ERROR_DRM_CANNOT_HANDLE;
+ if (in_sessionId.size() == 0) {
+ status = Status::BAD_VALUE;
+ }
+ return toNdkScopedAStatus(status);
+}
+
+::ndk::ScopedAStatus DrmPlugin::removeOfflineLicense(
+ const ::aidl::android::hardware::drm::KeySetId& in_keySetId) {
+ if (mMockError != Status::OK) {
+ return toNdkScopedAStatus(toMockStatus(mMockError));
+ }
+ Status status = Status::BAD_VALUE;
+ std::string licenseName(in_keySetId.keySetId.begin(), in_keySetId.keySetId.end());
+ if (mFileHandle.DeleteLicense(licenseName)) {
+ status = Status::OK;
+ }
+ return toNdkScopedAStatus(status);
+}
+
+::ndk::ScopedAStatus DrmPlugin::removeSecureStop(
+ const ::aidl::android::hardware::drm::SecureStopId& in_secureStopId) {
+ Mutex::Autolock lock(mSecureStopLock);
+
+ Status status = Status::OK;
+ if (1 != mSecureStops.erase(in_secureStopId.secureStopId)) {
+ status = Status::BAD_VALUE;
+ }
+ return toNdkScopedAStatus(status);
+}
+
+::ndk::ScopedAStatus DrmPlugin::requiresSecureDecoder(
+ const std::string& in_mime, ::aidl::android::hardware::drm::SecurityLevel in_level,
+ bool* _aidl_return) {
+ UNUSED(in_mime);
+ UNUSED(in_level);
+ *_aidl_return = false;
+ return ::ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus DrmPlugin::requiresSecureDecoderDefault(const std::string& in_mime,
+ bool* _aidl_return) {
+ UNUSED(in_mime);
+ // Clearkey only supports SW_SECURE_CRYPTO, so we always returns false
+ // regardless of mime type.
+ *_aidl_return = false;
+ return ::ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus DrmPlugin::restoreKeys(
+ const std::vector<uint8_t>& in_sessionId,
+ const ::aidl::android::hardware::drm::KeySetId& in_keySetId) {
+ if (in_sessionId.size() == 0 || in_keySetId.keySetId.size() == 0) {
+ return toNdkScopedAStatus(Status::BAD_VALUE);
+ }
+
+ DeviceFiles::LicenseState licenseState;
+ std::string offlineLicense;
+ if (!mFileHandle.RetrieveLicense(
+ std::string(in_keySetId.keySetId.begin(), in_keySetId.keySetId.end()),
+ &licenseState, &offlineLicense)) {
+ ALOGE("Failed to restore offline license");
+ return toNdkScopedAStatus(Status::ERROR_DRM_NO_LICENSE);
+ }
+
+ if (DeviceFiles::kLicenseStateUnknown == licenseState ||
+ DeviceFiles::kLicenseStateReleasing == licenseState) {
+ ALOGE("Invalid license state=%d", licenseState);
+ return toNdkScopedAStatus(Status::ERROR_DRM_NO_LICENSE);
+ }
+
+ ::android::sp<Session> session = mSessionLibrary->findSession(in_sessionId);
+ if (!session.get()) {
+ return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED);
+ }
+ auto res = session->provideKeyResponse(
+ std::vector<uint8_t>(offlineLicense.begin(), offlineLicense.end()));
+ if (res != clearkeydrm::OK) {
+ ALOGE("Failed to restore keys");
+ }
+ return toNdkScopedAStatus(res);
+}
+
+void DrmPlugin::sendEvent(::aidl::android::hardware::drm::EventType in_eventType,
+ const std::vector<uint8_t>& in_sessionId,
+ const std::vector<uint8_t>& in_data) {
+ if (mListener != nullptr) {
+ mListener->onEvent(in_eventType, in_sessionId, in_data);
+ } else if (mListener != nullptr) {
+ mListener->onEvent(in_eventType, in_sessionId, in_data);
+ } else {
+ ALOGE("Null event listener, event not sent");
+ }
+ return;
+}
+
+void DrmPlugin::sendExpirationUpdate(const std::vector<uint8_t>& in_sessionId,
+ int64_t in_expiryTimeInMS) {
+ if (mListener != nullptr) {
+ mListener->onExpirationUpdate(in_sessionId, in_expiryTimeInMS);
+ } else if (mListener != nullptr) {
+ mListener->onExpirationUpdate(in_sessionId, in_expiryTimeInMS);
+ } else {
+ ALOGE("Null event listener, event not sent");
+ }
+ return;
+}
+
+void DrmPlugin::sendKeysChange(
+ const std::vector<uint8_t>& in_sessionId,
+ const std::vector<::aidl::android::hardware::drm::KeyStatus>& in_keyStatusList,
+ bool in_hasNewUsableKey) {
+ if (mListener != nullptr) {
+ mListener->onKeysChange(in_sessionId, in_keyStatusList, in_hasNewUsableKey);
+ } else if (mListener != nullptr) {
+ mListener->onKeysChange(in_sessionId, in_keyStatusList, in_hasNewUsableKey);
+ } else {
+ ALOGE("Null event listener, event not sent");
+ }
+ return;
+}
+
+void DrmPlugin::sendSessionLostState(const std::vector<uint8_t>& in_sessionId) {
+ if (mListener != nullptr) {
+ mListener->onSessionLostState(in_sessionId);
+ }
+ return;
+}
+
+::ndk::ScopedAStatus DrmPlugin::setCipherAlgorithm(const std::vector<uint8_t>& /*in_sessionId*/,
+ const std::string& /*in_algorithm*/) {
+ return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE);
+}
+
+::ndk::ScopedAStatus DrmPlugin::setListener(
+ const std::shared_ptr<
+ ::aidl::android::hardware::drm::IDrmPluginListener>& in_listener) {
+ mListener = in_listener;
+ return toNdkScopedAStatus(Status::OK);
+}
+
+::ndk::ScopedAStatus DrmPlugin::setMacAlgorithm(const std::vector<uint8_t>& /*in_sessionId*/,
+ const std::string& /*in_algorithm*/) {
+ return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE);
+}
+
+::ndk::ScopedAStatus DrmPlugin::setPlaybackId(const std::vector<uint8_t>& in_sessionId,
+ const std::string& in_playbackId) {
+ if (in_sessionId.size() == 0) {
+ ALOGE("Invalid empty session id");
+ return toNdkScopedAStatus(Status::BAD_VALUE);
+ }
+
+ std::vector<uint8_t> sid = in_sessionId;
+ mPlaybackId[sid] = in_playbackId;
+ return toNdkScopedAStatus(Status::OK);
+}
+
+::ndk::ScopedAStatus DrmPlugin::setPropertyByteArray(const std::string& in_propertyName,
+ const std::vector<uint8_t>& in_value) {
+ if (in_propertyName == kDeviceIdKey) {
+ ALOGD("Cannot set immutable property: %s", in_propertyName.c_str());
+ return toNdkScopedAStatus(Status::BAD_VALUE);
+ } else if (in_propertyName == kClientIdKey) {
+ mByteArrayProperties[kClientIdKey] = in_value;
+ return toNdkScopedAStatus(Status::OK);
+ }
+
+ // Setting of undefined properties is not supported
+ ALOGE("Failed to set property byte array, key=%s", in_propertyName.c_str());
+ return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE);
+}
+
+::ndk::ScopedAStatus DrmPlugin::setPropertyString(const std::string& in_propertyName,
+ const std::string& in_value) {
+ std::string immutableKeys;
+ immutableKeys.append(kAlgorithmsKey + ",");
+ immutableKeys.append(kPluginDescriptionKey + ",");
+ immutableKeys.append(kVendorKey + ",");
+ immutableKeys.append(kVersionKey + ",");
+
+ std::string key = std::string(in_propertyName.c_str());
+ if (immutableKeys.find(key) != std::string::npos) {
+ ALOGD("Cannot set immutable property: %s", key.c_str());
+ return toNdkScopedAStatus(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 toNdkScopedAStatus(Status::BAD_VALUE);
+ }
+
+ if (in_propertyName == kDrmErrorTestKey) {
+ if (in_value == kResourceContentionValue) {
+ mMockError = Status::ERROR_DRM_RESOURCE_CONTENTION;
+ } else if (in_value == kLostStateValue) {
+ mMockError = Status::ERROR_DRM_SESSION_LOST_STATE;
+ } else if (in_value == kFrameTooLargeValue) {
+ mMockError = Status::ERROR_DRM_FRAME_TOO_LARGE;
+ } else if (in_value == kInvalidStateValue) {
+ mMockError = Status::ERROR_DRM_INVALID_STATE;
+ } else {
+ mMockError = Status::ERROR_DRM_UNKNOWN;
+ }
+ }
+
+ mStringProperties[key] = std::string(in_value.c_str());
+ return toNdkScopedAStatus(Status::OK);
+}
+
+::ndk::ScopedAStatus DrmPlugin::sign(const std::vector<uint8_t>& /*in_sessionId*/,
+ const std::vector<uint8_t>& /*in_keyId*/,
+ const std::vector<uint8_t>& /*in_message*/,
+ std::vector<uint8_t>* _aidl_return) {
+ *_aidl_return = {};
+ return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE);
+}
+
+::ndk::ScopedAStatus DrmPlugin::signRSA(const std::vector<uint8_t>& /*in_sessionId*/,
+ const std::string& /*in_algorithm*/,
+ const std::vector<uint8_t>& /*in_message*/,
+ const std::vector<uint8_t>& /*in_wrappedkey*/,
+ std::vector<uint8_t>* _aidl_return) {
+ *_aidl_return = {};
+ return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE);
+}
+
+::ndk::ScopedAStatus DrmPlugin::verify(const std::vector<uint8_t>& /*in_sessionId*/,
+ const std::vector<uint8_t>& /*in_keyId*/,
+ const std::vector<uint8_t>& /*in_message*/,
+ const std::vector<uint8_t>& /*in_signature*/,
+ bool* _aidl_return) {
+ *_aidl_return = false;
+ return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE);
+}
+
+// Private methods below.
+void DrmPlugin::setPlayPolicy() {
+ ::android::Mutex::Autolock lock(mPlayPolicyLock);
+ 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);
+}
+
+bool DrmPlugin::makeKeySetId(std::string* keySetId) {
+ if (!keySetId) {
+ ALOGE("keySetId destination not provided");
+ return false;
+ }
+ std::vector<uint8_t> ksid(kKeySetIdPrefix.begin(), kKeySetIdPrefix.end());
+ ksid.resize(kKeySetIdLength);
+ std::vector<uint8_t> randomData((kKeySetIdLength - kKeySetIdPrefix.size()) / 2, 0);
+
+ while (keySetId->empty()) {
+ for (auto itr = randomData.begin(); itr != randomData.end(); ++itr) {
+ *itr = std::rand() % 0xff;
+ }
+ auto id = reinterpret_cast<const uint8_t*>(randomData.data());
+ *keySetId = kKeySetIdPrefix + ::android::ByteArrayToHexString(id, randomData.size());
+ if (mFileHandle.LicenseExists(*keySetId)) {
+ // collision, regenerate
+ ALOGV("Retry generating KeySetId");
+ keySetId->clear();
+ }
+ }
+ return true;
+}
+
+Status DrmPlugin::setSecurityLevel(const std::vector<uint8_t>& sessionId, SecurityLevel level) {
+ if (sessionId.size() == 0) {
+ ALOGE("Invalid empty session id");
+ return Status::BAD_VALUE;
+ }
+
+ if (level != SecurityLevel::DEFAULT && level != SecurityLevel::SW_SECURE_CRYPTO) {
+ ALOGE("Cannot set security level > max");
+ return Status::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ std::vector<uint8_t> sid = sessionId;
+ ::android::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 drm
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/drm/mediadrm/plugins/clearkey/aidl/Service.cpp b/drm/mediadrm/plugins/clearkey/aidl/Service.cpp
new file mode 100644
index 0000000..7d342f3
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/aidl/Service.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2021 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 1
+#define LOG_TAG "clearkey-main"
+
+#include "CreatePluginFactories.h"
+
+#include <android-base/logging.h>
+
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+using ::android::base::InitLogging;
+using ::android::base::LogdLogger;
+
+using ::aidl::android::hardware::drm::clearkey::createCryptoFactory;
+using ::aidl::android::hardware::drm::clearkey::createDrmFactory;
+using ::aidl::android::hardware::drm::clearkey::CryptoFactory;
+using ::aidl::android::hardware::drm::clearkey::DrmFactory;
+
+int main(int /*argc*/, char* argv[]) {
+ InitLogging(argv, LogdLogger());
+ ::android::base::SetMinimumLogSeverity(::android::base::VERBOSE);
+ ABinderProcess_setThreadPoolMaxThreadCount(8);
+
+ std::shared_ptr<DrmFactory> drmFactory = createDrmFactory();
+ const std::string drmInstance = std::string() + DrmFactory::descriptor + "/clearkey";
+ binder_status_t status =
+ AServiceManager_addService(drmFactory->asBinder().get(), drmInstance.c_str());
+ CHECK(status == STATUS_OK);
+
+ std::shared_ptr<CryptoFactory> cryptoFactory = createCryptoFactory();
+ const std::string cryptoInstance = std::string() + CryptoFactory::descriptor + "/clearkey";
+ status = AServiceManager_addService(cryptoFactory->asBinder().get(), cryptoInstance.c_str());
+ CHECK(status == STATUS_OK);
+
+ ABinderProcess_joinThreadPool();
+ return EXIT_FAILURE; // should not reached
+}
diff --git a/drm/mediadrm/plugins/clearkey/aidl/ServiceLazy.cpp b/drm/mediadrm/plugins/clearkey/aidl/ServiceLazy.cpp
new file mode 100644
index 0000000..55aa6e0
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/aidl/ServiceLazy.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2021 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 1
+#define LOG_TAG "clearkey-main"
+
+#include "CreatePluginFactories.h"
+
+#include <android-base/logging.h>
+
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+using ::android::base::InitLogging;
+using ::android::base::StderrLogger;
+
+using ::aidl::android::hardware::drm::clearkey::createCryptoFactory;
+using ::aidl::android::hardware::drm::clearkey::createDrmFactory;
+using ::aidl::android::hardware::drm::clearkey::CryptoFactory;
+using ::aidl::android::hardware::drm::clearkey::DrmFactory;
+
+int main(int /*argc*/, char* argv[]) {
+ InitLogging(argv, StderrLogger);
+ ::android::base::SetMinimumLogSeverity(::android::base::VERBOSE);
+ ABinderProcess_setThreadPoolMaxThreadCount(8);
+
+ binder_status_t status{};
+ std::shared_ptr<DrmFactory> drmFactory = createDrmFactory();
+ const std::string drmInstance = std::string() + DrmFactory::descriptor + "/clearkey";
+ status = AServiceManager_registerLazyService(drmFactory->asBinder().get(),
+ drmInstance.c_str());
+ CHECK(status == STATUS_OK);
+
+ std::shared_ptr<CryptoFactory> cryptoFactory = createCryptoFactory();
+ const std::string cryptoInstance = std::string() + CryptoFactory::descriptor + "/clearkey";
+ status = AServiceManager_registerLazyService(cryptoFactory->asBinder().get(),
+ cryptoInstance.c_str());
+ CHECK(status == STATUS_OK);
+
+ ABinderProcess_joinThreadPool();
+ return EXIT_FAILURE; // should not reached
+}
diff --git a/drm/mediadrm/plugins/clearkey/aidl/android.hardware.drm-service-lazy.clearkey.rc b/drm/mediadrm/plugins/clearkey/aidl/android.hardware.drm-service-lazy.clearkey.rc
new file mode 100644
index 0000000..019c726
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/aidl/android.hardware.drm-service-lazy.clearkey.rc
@@ -0,0 +1,9 @@
+service vendor.drm-clearkey-service /vendor/bin/hw/android.hardware.drm-service.clearkey
+ disabled
+ class hal
+ user media
+ group mediadrm drmrpc
+ ioprio rt 4
+ task_profiles ProcessCapacityHigh
+ interface aidl android.hardware.drm.IDrmFactory/clearkey
+ interface aidl android.hardware.drm.ICryptoFactory/clearkey
diff --git a/drm/mediadrm/plugins/clearkey/aidl/android.hardware.drm-service.clearkey.rc b/drm/mediadrm/plugins/clearkey/aidl/android.hardware.drm-service.clearkey.rc
new file mode 100644
index 0000000..2b2637f
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/aidl/android.hardware.drm-service.clearkey.rc
@@ -0,0 +1,8 @@
+service vendor.drm-clearkey-service /vendor/bin/hw/android.hardware.drm-service.clearkey
+ class hal
+ user media
+ group mediadrm drmrpc
+ ioprio rt 4
+ task_profiles ProcessCapacityHigh
+ interface aidl android.hardware.drm.IDrmFactory/clearkey
+ interface aidl android.hardware.drm.ICryptoFactory/clearkey
diff --git a/drm/mediadrm/plugins/clearkey/aidl/android.hardware.drm-service.clearkey.xml b/drm/mediadrm/plugins/clearkey/aidl/android.hardware.drm-service.clearkey.xml
new file mode 100644
index 0000000..73c15f3
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/aidl/android.hardware.drm-service.clearkey.xml
@@ -0,0 +1,8 @@
+<manifest version="1.0" type="device">
+ <hal format="aidl">
+ <name>android.hardware.drm</name>
+ <version>1</version>
+ <fqname>ICryptoFactory/clearkey</fqname>
+ <fqname>IDrmFactory/clearkey</fqname>
+ </hal>
+</manifest>
diff --git a/drm/mediadrm/plugins/clearkey/aidl/include/AidlUtils.h b/drm/mediadrm/plugins/clearkey/aidl/include/AidlUtils.h
new file mode 100644
index 0000000..0370ebe
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/aidl/include/AidlUtils.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include <android/binder_auto_utils.h>
+#include "aidl/android/hardware/drm/Status.h"
+#include "ClearKeyTypes.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace drm {
+namespace clearkey {
+
+inline ::aidl::android::hardware::drm::Status toMockStatus(
+ ::aidl::android::hardware::drm::Status status) {
+ switch (status) {
+ case ::aidl::android::hardware::drm::Status::ERROR_DRM_INSUFFICIENT_SECURITY:
+ case ::aidl::android::hardware::drm::Status::ERROR_DRM_FRAME_TOO_LARGE:
+ case ::aidl::android::hardware::drm::Status::ERROR_DRM_SESSION_LOST_STATE:
+ return ::aidl::android::hardware::drm::Status::ERROR_DRM_UNKNOWN;
+ default:
+ return status;
+ }
+}
+
+inline ::ndk::ScopedAStatus toNdkScopedAStatus(::aidl::android::hardware::drm::Status status) {
+ if (Status::OK == status) {
+ return ::ndk::ScopedAStatus::ok();
+ } else {
+ return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(status));
+ }
+}
+
+inline ::ndk::ScopedAStatus toNdkScopedAStatus(clearkeydrm::CdmResponseType res) {
+ return toNdkScopedAStatus(static_cast<::aidl::android::hardware::drm::Status>(res));
+}
+
+#define UNUSED(x) (void)(x);
+
+} // namespace clearkey
+} // namespace drm
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/drm/mediadrm/plugins/clearkey/aidl/include/CreatePluginFactories.h b/drm/mediadrm/plugins/clearkey/aidl/include/CreatePluginFactories.h
new file mode 100644
index 0000000..5a90fb8
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/aidl/include/CreatePluginFactories.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+#pragma once
+
+#include "CryptoFactory.h"
+#include "DrmFactory.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace drm {
+namespace clearkey {
+//extern "C" {
+std::shared_ptr<DrmFactory> createDrmFactory();
+std::shared_ptr<CryptoFactory> createCryptoFactory();
+//}
+
+} // namespace clearkey
+} // namespace drm
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/drm/mediadrm/plugins/clearkey/aidl/include/CryptoFactory.h b/drm/mediadrm/plugins/clearkey/aidl/include/CryptoFactory.h
new file mode 100644
index 0000000..0d6c4ce
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/aidl/include/CryptoFactory.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+#pragma once
+
+#include <aidl/android/hardware/drm/BnCryptoFactory.h>
+#include <aidl/android/hardware/drm/ICryptoFactory.h>
+#include <aidl/android/hardware/drm/ICryptoPlugin.h>
+
+#include "ClearKeyTypes.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace drm {
+namespace clearkey {
+
+struct CryptoFactory : public BnCryptoFactory {
+ CryptoFactory() {}
+ virtual ~CryptoFactory() {}
+
+ ::ndk::ScopedAStatus createPlugin(
+ const ::aidl::android::hardware::drm::Uuid& in_uuid,
+ const std::vector<uint8_t>& in_initData,
+ std::shared_ptr<::aidl::android::hardware::drm::ICryptoPlugin>* _aidl_return) override;
+
+ ::ndk::ScopedAStatus isCryptoSchemeSupported(
+ const ::aidl::android::hardware::drm::Uuid& in_uuid, bool* _aidl_return) override;
+
+ private:
+ CLEARKEY_DISALLOW_COPY_AND_ASSIGN(CryptoFactory);
+};
+
+} // namespace clearkey
+} // namespace drm
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/drm/mediadrm/plugins/clearkey/aidl/include/CryptoPlugin.h b/drm/mediadrm/plugins/clearkey/aidl/include/CryptoPlugin.h
new file mode 100644
index 0000000..f98829d
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/aidl/include/CryptoPlugin.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+#pragma once
+
+#include <aidl/android/hardware/drm/BnCryptoPlugin.h>
+#include <aidl/android/hardware/drm/Status.h>
+
+#include <aidl/android/hardware/common/Ashmem.h>
+
+#include <android/binder_auto_utils.h>
+
+#include <memory>
+#include <mutex>
+
+#include "ClearKeyTypes.h"
+#include "Session.h"
+
+namespace {
+static const size_t KEY_ID_SIZE = 16;
+static const size_t KEY_IV_SIZE = 16;
+} // namespace
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace drm {
+namespace clearkey {
+
+using namespace clearkeydrm;
+using ::aidl::android::hardware::drm::Status;
+
+struct SharedBufferBase {
+ uint8_t* mBase;
+ int64_t mSize;
+ SharedBufferBase(const ::aidl::android::hardware::common::Ashmem& mem);
+ ~SharedBufferBase();
+};
+
+struct CryptoPlugin : public BnCryptoPlugin {
+ explicit CryptoPlugin(const std::vector<uint8_t>& sessionId) {
+ const auto res = setMediaDrmSession(sessionId);
+ mInitStatus = Status::OK;
+ if (!res.isOk() && res.getExceptionCode() == EX_SERVICE_SPECIFIC) {
+ mInitStatus = static_cast<Status>(res.getServiceSpecificError());
+ }
+ }
+ virtual ~CryptoPlugin() {}
+
+ ::ndk::ScopedAStatus decrypt(
+ bool in_secure, const std::vector<uint8_t>& in_keyId, const std::vector<uint8_t>& in_iv,
+ ::aidl::android::hardware::drm::Mode in_mode,
+ const ::aidl::android::hardware::drm::Pattern& in_pattern,
+ const std::vector<::aidl::android::hardware::drm::SubSample>& in_subSamples,
+ const ::aidl::android::hardware::drm::SharedBuffer& in_source, int64_t in_offset,
+ const ::aidl::android::hardware::drm::DestinationBuffer& in_destination,
+ ::aidl::android::hardware::drm::DecryptResult* _aidl_return) override;
+
+ ::ndk::ScopedAStatus getLogMessages(
+ std::vector<::aidl::android::hardware::drm::LogMessage>* _aidl_return) override;
+
+ ::ndk::ScopedAStatus notifyResolution(int32_t in_width, int32_t in_height) override;
+
+ ::ndk::ScopedAStatus requiresSecureDecoderComponent(const std::string& in_mime,
+ bool* _aidl_return) override;
+
+ ::ndk::ScopedAStatus setMediaDrmSession(const std::vector<uint8_t>& in_sessionId) override;
+
+ ::ndk::ScopedAStatus setSharedBufferBase(
+ const ::aidl::android::hardware::common::Ashmem& in_base, int32_t in_bufferId) override;
+
+ ::aidl::android::hardware::drm::Status getInitStatus() const { return mInitStatus; }
+
+ private:
+ CLEARKEY_DISALLOW_COPY_AND_ASSIGN(CryptoPlugin);
+
+ std::mutex mSharedBufferLock;
+ std::map<uint32_t, std::shared_ptr<SharedBufferBase>> mSharedBufferMap
+ GUARDED_BY(mSharedBufferLock);
+ ::android::sp<Session> mSession;
+ ::aidl::android::hardware::drm::Status mInitStatus;
+};
+
+} // namespace clearkey
+} // namespace drm
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/drm/mediadrm/plugins/clearkey/aidl/include/DrmFactory.h b/drm/mediadrm/plugins/clearkey/aidl/include/DrmFactory.h
new file mode 100644
index 0000000..0143dc7
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/aidl/include/DrmFactory.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+#pragma once
+
+#include <aidl/android/hardware/drm/BnDrmFactory.h>
+#include <aidl/android/hardware/drm/IDrmFactory.h>
+#include <aidl/android/hardware/drm/IDrmPlugin.h>
+
+#include <string>
+#include <vector>
+
+#include "ClearKeyTypes.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace drm {
+namespace clearkey {
+
+struct DrmFactory : public BnDrmFactory {
+ DrmFactory() {}
+ virtual ~DrmFactory() {}
+
+ ::ndk::ScopedAStatus createPlugin(
+ const ::aidl::android::hardware::drm::Uuid& in_uuid,
+ const std::string& in_appPackageName,
+ std::shared_ptr<::aidl::android::hardware::drm::IDrmPlugin>* _aidl_return) override;
+
+ ::ndk::ScopedAStatus getSupportedCryptoSchemes(
+ std::vector<::aidl::android::hardware::drm::Uuid>* _aidl_return) override;
+
+ ::ndk::ScopedAStatus isContentTypeSupported(const std::string& in_mimeType,
+ bool* _aidl_return) override;
+
+ ::ndk::ScopedAStatus isCryptoSchemeSupported(
+ const ::aidl::android::hardware::drm::Uuid& in_uuid, const std::string& in_mimeType,
+ ::aidl::android::hardware::drm::SecurityLevel in_securityLevel,
+ bool* _aidl_return) override;
+
+ binder_status_t dump(int fd, const char** args, uint32_t numArgs) override;
+
+
+ private:
+ CLEARKEY_DISALLOW_COPY_AND_ASSIGN(DrmFactory);
+};
+
+} // namespace clearkey
+} // namespace drm
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/drm/mediadrm/plugins/clearkey/aidl/include/DrmPlugin.h b/drm/mediadrm/plugins/clearkey/aidl/include/DrmPlugin.h
new file mode 100644
index 0000000..44db1d5
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/aidl/include/DrmPlugin.h
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+#pragma once
+
+#include <aidl/android/hardware/drm/BnDrmPlugin.h>
+#include <aidl/android/hardware/drm/IDrmPluginListener.h>
+#include <aidl/android/hardware/drm/Status.h>
+
+#include <stdio.h>
+#include <map>
+
+#include <utils/List.h>
+
+#include "DeviceFiles.h"
+#include "SessionLibrary.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace drm {
+namespace clearkey {
+
+using namespace clearkeydrm;
+using ::aidl::android::hardware::drm::KeyType;
+using ::aidl::android::hardware::drm::Status;
+
+struct DrmPlugin : public BnDrmPlugin {
+ public:
+ explicit DrmPlugin(SessionLibrary* sessionLibrary);
+ virtual ~DrmPlugin() { mFileHandle.DeleteAllLicenses(); }
+
+ ::ndk::ScopedAStatus closeSession(const std::vector<uint8_t>& in_sessionId) override;
+ ::ndk::ScopedAStatus decrypt(const std::vector<uint8_t>& in_sessionId,
+ const std::vector<uint8_t>& in_keyId,
+ const std::vector<uint8_t>& in_input,
+ const std::vector<uint8_t>& in_iv,
+ std::vector<uint8_t>* _aidl_return) override;
+ ::ndk::ScopedAStatus encrypt(const std::vector<uint8_t>& in_sessionId,
+ const std::vector<uint8_t>& in_keyId,
+ const std::vector<uint8_t>& in_input,
+ const std::vector<uint8_t>& in_iv,
+ std::vector<uint8_t>* _aidl_return) override;
+ ::ndk::ScopedAStatus getHdcpLevels(
+ ::aidl::android::hardware::drm::HdcpLevels* _aidl_return) override;
+ ::ndk::ScopedAStatus getKeyRequest(
+ const std::vector<uint8_t>& in_scope, const std::vector<uint8_t>& in_initData,
+ const std::string& in_mimeType, ::aidl::android::hardware::drm::KeyType in_keyType,
+ const std::vector<::aidl::android::hardware::drm::KeyValue>& in_optionalParameters,
+ ::aidl::android::hardware::drm::KeyRequest* _aidl_return) override;
+ ::ndk::ScopedAStatus getLogMessages(
+ std::vector<::aidl::android::hardware::drm::LogMessage>* _aidl_return) override;
+
+ ::ndk::ScopedAStatus getMetrics(
+ std::vector<::aidl::android::hardware::drm::DrmMetricGroup>* _aidl_return) override;
+ ::ndk::ScopedAStatus getNumberOfSessions(
+ ::aidl::android::hardware::drm::NumberOfSessions* _aidl_return) override;
+ ::ndk::ScopedAStatus getOfflineLicenseKeySetIds(
+ std::vector<::aidl::android::hardware::drm::KeySetId>* _aidl_return) override;
+ ::ndk::ScopedAStatus getOfflineLicenseState(
+ const ::aidl::android::hardware::drm::KeySetId& in_keySetId,
+ ::aidl::android::hardware::drm::OfflineLicenseState* _aidl_return) override;
+ ::ndk::ScopedAStatus getPropertyByteArray(const std::string& in_propertyName,
+ std::vector<uint8_t>* _aidl_return) override;
+ ::ndk::ScopedAStatus getPropertyString(const std::string& in_propertyName,
+ std::string* _aidl_return) override;
+ ::ndk::ScopedAStatus getProvisionRequest(
+ const std::string& in_certificateType, const std::string& in_certificateAuthority,
+ ::aidl::android::hardware::drm::ProvisionRequest* _aidl_return) override;
+ ::ndk::ScopedAStatus getSecureStop(
+ const ::aidl::android::hardware::drm::SecureStopId& in_secureStopId,
+ ::aidl::android::hardware::drm::SecureStop* _aidl_return) override;
+ ::ndk::ScopedAStatus getSecureStopIds(
+ std::vector<::aidl::android::hardware::drm::SecureStopId>* _aidl_return) override;
+ ::ndk::ScopedAStatus getSecureStops(
+ std::vector<::aidl::android::hardware::drm::SecureStop>* _aidl_return) override;
+ ::ndk::ScopedAStatus getSecurityLevel(
+ const std::vector<uint8_t>& in_sessionId,
+ ::aidl::android::hardware::drm::SecurityLevel* _aidl_return) override;
+ ::ndk::ScopedAStatus openSession(::aidl::android::hardware::drm::SecurityLevel in_securityLevel,
+ std::vector<uint8_t>* _aidl_return) override;
+ ::ndk::ScopedAStatus provideKeyResponse(
+ const std::vector<uint8_t>& in_scope, const std::vector<uint8_t>& in_response,
+ ::aidl::android::hardware::drm::KeySetId* _aidl_return) override;
+ ::ndk::ScopedAStatus provideProvisionResponse(
+ const std::vector<uint8_t>& in_response,
+ ::aidl::android::hardware::drm::ProvideProvisionResponseResult* _aidl_return) override;
+ ::ndk::ScopedAStatus queryKeyStatus(
+ const std::vector<uint8_t>& in_sessionId,
+ std::vector<::aidl::android::hardware::drm::KeyValue>* _aidl_return) override;
+ ::ndk::ScopedAStatus releaseAllSecureStops() override;
+ ::ndk::ScopedAStatus releaseSecureStop(
+ const ::aidl::android::hardware::drm::SecureStopId& in_secureStopId) override;
+ ::ndk::ScopedAStatus releaseSecureStops(
+ const ::aidl::android::hardware::drm::OpaqueData& in_ssRelease) override;
+ ::ndk::ScopedAStatus removeAllSecureStops() override;
+ ::ndk::ScopedAStatus removeKeys(const std::vector<uint8_t>& in_sessionId) override;
+ ::ndk::ScopedAStatus removeOfflineLicense(
+ const ::aidl::android::hardware::drm::KeySetId& in_keySetId) override;
+ ::ndk::ScopedAStatus removeSecureStop(
+ const ::aidl::android::hardware::drm::SecureStopId& in_secureStopId) override;
+ ::ndk::ScopedAStatus requiresSecureDecoder(
+ const std::string& in_mime, ::aidl::android::hardware::drm::SecurityLevel in_level,
+ bool* _aidl_return) override;
+ ::ndk::ScopedAStatus requiresSecureDecoderDefault(const std::string& in_mime,
+ bool* _aidl_return) override;
+ ::ndk::ScopedAStatus restoreKeys(
+ const std::vector<uint8_t>& in_sessionId,
+ const ::aidl::android::hardware::drm::KeySetId& in_keySetId) override;
+ ::ndk::ScopedAStatus setCipherAlgorithm(const std::vector<uint8_t>& in_sessionId,
+ const std::string& in_algorithm) override;
+ ::ndk::ScopedAStatus setListener(
+ // const ::android::sp<::aidl::android::hardware::drm::IDrmPluginListener>&
+ // in_listener)
+ const std::shared_ptr<IDrmPluginListener>& in_listener) override;
+ ::ndk::ScopedAStatus setMacAlgorithm(const std::vector<uint8_t>& in_sessionId,
+ const std::string& in_algorithm) override;
+ ::ndk::ScopedAStatus setPlaybackId(const std::vector<uint8_t>& in_sessionId,
+ const std::string& in_playbackId) override;
+ ::ndk::ScopedAStatus setPropertyByteArray(const std::string& in_propertyName,
+ const std::vector<uint8_t>& in_value) override;
+ ::ndk::ScopedAStatus setPropertyString(const std::string& in_propertyName,
+ const std::string& in_value) override;
+ ::ndk::ScopedAStatus sign(const std::vector<uint8_t>& in_sessionId,
+ const std::vector<uint8_t>& in_keyId,
+ const std::vector<uint8_t>& in_message,
+ std::vector<uint8_t>* _aidl_return) override;
+ ::ndk::ScopedAStatus signRSA(const std::vector<uint8_t>& in_sessionId,
+ const std::string& in_algorithm,
+ const std::vector<uint8_t>& in_message,
+ const std::vector<uint8_t>& in_wrappedkey,
+ std::vector<uint8_t>* _aidl_return) override;
+ ::ndk::ScopedAStatus verify(const std::vector<uint8_t>& in_sessionId,
+ const std::vector<uint8_t>& in_keyId,
+ const std::vector<uint8_t>& in_message,
+ const std::vector<uint8_t>& in_signature,
+ bool* _aidl_return) override;
+
+ private:
+ void initProperties();
+ void installSecureStop(const std::vector<uint8_t>& sessionId);
+ bool makeKeySetId(std::string* keySetId);
+ void setPlayPolicy();
+
+ void sendEvent(::aidl::android::hardware::drm::EventType in_eventType,
+ const std::vector<uint8_t>& in_sessionId,
+ const std::vector<uint8_t>& in_data);
+ void sendExpirationUpdate(const std::vector<uint8_t>& in_sessionId,
+ int64_t in_expiryTimeInMS);
+ void sendKeysChange(
+ const std::vector<uint8_t>& in_sessionId,
+ const std::vector<::aidl::android::hardware::drm::KeyStatus>& in_keyStatusList,
+ bool in_hasNewUsableKey);
+ void sendSessionLostState(const std::vector<uint8_t>& in_sessionId);
+
+ Status setSecurityLevel(const std::vector<uint8_t>& sessionId, SecurityLevel level);
+
+ Status getKeyRequestCommon(const std::vector<uint8_t>& scope,
+ const std::vector<uint8_t>& initData, const std::string& mimeType,
+ KeyType keyType, const std::vector<KeyValue>& optionalParameters,
+ std::vector<uint8_t>* request, KeyRequestType* getKeyRequestType,
+ std::string* defaultUrl);
+
+ struct ClearkeySecureStop {
+ std::vector<uint8_t> id;
+ std::vector<uint8_t> data;
+ };
+
+ std::map<std::vector<uint8_t>, ClearkeySecureStop> mSecureStops;
+ std::vector<KeyValue> mPlayPolicy;
+ std::map<std::string, std::string> mStringProperties;
+ std::map<std::string, std::vector<uint8_t>> mByteArrayProperties;
+ std::map<std::string, std::vector<uint8_t>> mReleaseKeysMap;
+ std::map<std::vector<uint8_t>, std::string> mPlaybackId;
+ std::map<std::vector<uint8_t>, SecurityLevel> mSecurityLevel;
+ ::std::shared_ptr<IDrmPluginListener> mListener;
+ SessionLibrary* mSessionLibrary;
+ int64_t mOpenSessionOkCount;
+ int64_t mCloseSessionOkCount;
+ int64_t mCloseSessionNotOpenedCount;
+ uint32_t mNextSecureStopId;
+ ::android::Mutex mPlayPolicyLock;
+
+ // set by property to mock error scenarios
+ Status mMockError;
+
+ void processMockError(const ::android::sp<Session>& session) {
+ session->setMockError(static_cast<CdmResponseType>(mMockError));
+ mMockError = Status::OK;
+ }
+
+ DeviceFiles mFileHandle;
+ ::android::Mutex mSecureStopLock;
+
+ CLEARKEY_DISALLOW_COPY_AND_ASSIGN_AND_NEW(DrmPlugin);
+};
+
+} // namespace clearkey
+} // namespace drm
+} // namespace hardware
+} // namespace android
+} // namespace aidl
diff --git a/drm/mediadrm/plugins/clearkey/common/AesCtrDecryptor.cpp b/drm/mediadrm/plugins/clearkey/common/AesCtrDecryptor.cpp
new file mode 100644
index 0000000..0b97820
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/common/AesCtrDecryptor.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2021 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 "clearkey-AesDecryptor"
+
+#include <utils/Log.h>
+
+#include <openssl/aes.h>
+
+#include "AesCtrDecryptor.h"
+#include "ClearKeyTypes.h"
+
+namespace clearkeydrm {
+
+static const size_t kBlockBitCount = kBlockSize * 8;
+
+CdmResponseType AesCtrDecryptor::decrypt(const std::vector<uint8_t>& key, const Iv iv,
+ const uint8_t* source, uint8_t* destination,
+ const std::vector<int32_t>& clearDataLengths,
+ const std::vector<int32_t>& encryptedDataLengths,
+ size_t* bytesDecryptedOut) {
+
+ if (key.size() != kBlockSize || clearDataLengths.size() != encryptedDataLengths.size()) {
+ android_errorWriteLog(0x534e4554, "63982768");
+ return clearkeydrm::ERROR_DECRYPT;
+ }
+
+ uint32_t blockOffset = 0;
+ uint8_t previousEncryptedCounter[kBlockSize];
+ memset(previousEncryptedCounter, 0, kBlockSize);
+
+ 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 < clearDataLengths.size(); ++i) {
+ int32_t numBytesOfClearData = clearDataLengths[i];
+ if (numBytesOfClearData > 0) {
+ memcpy(destination + offset, source + offset, numBytesOfClearData);
+ offset += numBytesOfClearData;
+ }
+
+ int32_t numBytesOfEncryptedData = encryptedDataLengths[i];
+ if (numBytesOfEncryptedData > 0) {
+ AES_ctr128_encrypt(source + offset, destination + offset,
+ numBytesOfEncryptedData, &opensslKey, opensslIv,
+ previousEncryptedCounter, &blockOffset);
+ offset += numBytesOfEncryptedData;
+ }
+ }
+
+ *bytesDecryptedOut = offset;
+ return clearkeydrm::OK;
+}
+
+} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/common/Android.bp b/drm/mediadrm/plugins/clearkey/common/Android.bp
index 7ed8b88..1564133 100644
--- a/drm/mediadrm/plugins/clearkey/common/Android.bp
+++ b/drm/mediadrm/plugins/clearkey/common/Android.bp
@@ -44,3 +44,53 @@
integer_overflow: true,
},
}
+
+cc_library_static {
+ name: "libclearkeydevicefiles-protos.common",
+ vendor: true,
+
+ proto: {
+ export_proto_headers: true,
+ type: "lite",
+ },
+ srcs: ["protos/DeviceFiles.proto"],
+}
+
+cc_library_static {
+ name: "libclearkeybase",
+ vendor: true,
+
+ srcs: [
+ "AesCtrDecryptor.cpp",
+ "Base64.cpp",
+ "Buffer.cpp",
+ "ClearKeyUUID.cpp",
+ "DeviceFiles.cpp",
+ "InitDataParser.cpp",
+ "JsonWebKey.cpp",
+ "MemoryFileSystem.cpp",
+ "Session.cpp",
+ "SessionLibrary.cpp",
+ "Utils.cpp",
+ ],
+
+ cflags: ["-Wall", "-Werror"],
+
+ include_dirs: ["frameworks/av/include"],
+
+ shared_libs: [
+ "libutils",
+ "libcrypto",
+ ],
+
+ whole_static_libs: [
+ "libjsmn",
+ "libclearkeydevicefiles-protos.common",
+ ],
+
+ export_include_dirs: ["include"],
+
+ sanitize: {
+ integer_overflow: true,
+ },
+}
diff --git a/drm/mediadrm/plugins/clearkey/common/Base64.cpp b/drm/mediadrm/plugins/clearkey/common/Base64.cpp
new file mode 100644
index 0000000..6499793
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/common/Base64.cpp
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2021 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 "clearkey-Base64"
+
+#include "Base64.h"
+
+#include <string>
+
+namespace clearkeydrm {
+
+::android::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;
+
+ ::android::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 clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/common/Buffer.cpp b/drm/mediadrm/plugins/clearkey/common/Buffer.cpp
new file mode 100644
index 0000000..1671598
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/common/Buffer.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2021 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"
+
+namespace clearkeydrm {
+
+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 clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/common/DeviceFiles.cpp b/drm/mediadrm/plugins/clearkey/common/DeviceFiles.cpp
new file mode 100644
index 0000000..2299249
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/common/DeviceFiles.cpp
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2021 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 <utils/Log.h>
+
+#include <sys/stat.h>
+#include <string>
+
+#include "DeviceFiles.h"
+#include "protos/DeviceFiles.pb.h"
+
+#include <openssl/sha.h>
+
+// Protobuf generated classes.
+using clearkeydrm::HashedFile;
+using clearkeydrm::License;
+using clearkeydrm::License_LicenseState_ACTIVE;
+using clearkeydrm::License_LicenseState_RELEASING;
+using clearkeydrm::OfflineFile;
+
+namespace {
+const char kLicenseFileNameExt[] = ".lic";
+
+bool Hash(const std::string& data, std::string* hash) {
+ if (!hash) return false;
+
+ hash->resize(SHA256_DIGEST_LENGTH);
+
+ const unsigned char* input = reinterpret_cast<const unsigned char*>(data.data());
+ unsigned char* output = reinterpret_cast<unsigned char*>(&(*hash)[0]);
+ SHA256(input, data.size(), output);
+ return true;
+}
+
+} // namespace
+
+namespace clearkeydrm {
+
+bool DeviceFiles::StoreLicense(const std::string& keySetId, LicenseState state,
+ const std::string& licenseResponse) {
+ OfflineFile file;
+ file.set_type(OfflineFile::LICENSE);
+ file.set_version(OfflineFile::VERSION_1);
+
+ License* license = file.mutable_license();
+ switch (state) {
+ case kLicenseStateActive:
+ license->set_state(License_LicenseState_ACTIVE);
+ license->set_license(licenseResponse);
+ break;
+ case kLicenseStateReleasing:
+ license->set_state(License_LicenseState_RELEASING);
+ license->set_license(licenseResponse);
+ break;
+ default:
+ ALOGW("StoreLicense: Unknown license state: %u", state);
+ return false;
+ }
+
+ std::string serializedFile;
+ file.SerializeToString(&serializedFile);
+
+ return StoreFileWithHash(keySetId + kLicenseFileNameExt, serializedFile);
+}
+
+bool DeviceFiles::StoreFileWithHash(const std::string& fileName,
+ const std::string& serializedFile) {
+ std::string hash;
+ if (!Hash(serializedFile, &hash)) {
+ ALOGE("StoreFileWithHash: Failed to compute hash");
+ return false;
+ }
+
+ HashedFile hashFile;
+ hashFile.set_file(serializedFile);
+ hashFile.set_hash(hash);
+
+ std::string serializedHashFile;
+ hashFile.SerializeToString(&serializedHashFile);
+
+ return StoreFileRaw(fileName, serializedHashFile);
+}
+
+bool DeviceFiles::StoreFileRaw(const std::string& fileName, const std::string& serializedHashFile) {
+ MemoryFileSystem::MemoryFile memFile;
+ memFile.setFileName(fileName);
+ memFile.setContent(serializedHashFile);
+ memFile.setFileSize(serializedHashFile.size());
+ size_t len = mFileHandle.Write(fileName, memFile);
+
+ if (len != static_cast<size_t>(serializedHashFile.size())) {
+ ALOGE("StoreFileRaw: Failed to write %s", fileName.c_str());
+ ALOGD("StoreFileRaw: expected=%zd, actual=%zu", serializedHashFile.size(), len);
+ return false;
+ }
+
+ ALOGD("StoreFileRaw: wrote %zu bytes to %s", serializedHashFile.size(), fileName.c_str());
+ return true;
+}
+
+bool DeviceFiles::RetrieveLicense(const std::string& keySetId, LicenseState* state,
+ std::string* offlineLicense) {
+ OfflineFile file;
+ if (!RetrieveHashedFile(keySetId + kLicenseFileNameExt, &file)) {
+ return false;
+ }
+
+ if (file.type() != OfflineFile::LICENSE) {
+ ALOGE("RetrieveLicense: Invalid file type");
+ return false;
+ }
+
+ if (file.version() != OfflineFile::VERSION_1) {
+ ALOGE("RetrieveLicense: Invalid file version");
+ return false;
+ }
+
+ if (!file.has_license()) {
+ ALOGE("RetrieveLicense: License not present");
+ return false;
+ }
+
+ License license = file.license();
+ switch (license.state()) {
+ case License_LicenseState_ACTIVE:
+ *state = kLicenseStateActive;
+ break;
+ case License_LicenseState_RELEASING:
+ *state = kLicenseStateReleasing;
+ break;
+ default:
+ ALOGW("RetrieveLicense: Unrecognized license state: %u", kLicenseStateUnknown);
+ *state = kLicenseStateUnknown;
+ break;
+ }
+ *offlineLicense = license.license();
+ return true;
+}
+
+bool DeviceFiles::DeleteLicense(const std::string& keySetId) {
+ return mFileHandle.RemoveFile(keySetId + kLicenseFileNameExt);
+}
+
+bool DeviceFiles::DeleteAllLicenses() {
+ return mFileHandle.RemoveAllFiles();
+}
+
+bool DeviceFiles::LicenseExists(const std::string& keySetId) {
+ return mFileHandle.FileExists(keySetId + kLicenseFileNameExt);
+}
+
+std::vector<std::string> DeviceFiles::ListLicenses() const {
+ std::vector<std::string> licenses = mFileHandle.ListFiles();
+ for (size_t i = 0; i < licenses.size(); i++) {
+ std::string& license = licenses[i];
+ license = license.substr(0, license.size() - strlen(kLicenseFileNameExt));
+ }
+ return licenses;
+}
+
+bool DeviceFiles::RetrieveHashedFile(const std::string& fileName, OfflineFile* deSerializedFile) {
+ if (!deSerializedFile) {
+ ALOGE("RetrieveHashedFile: invalid file parameter");
+ return false;
+ }
+
+ if (!FileExists(fileName)) {
+ ALOGE("RetrieveHashedFile: %s does not exist", fileName.c_str());
+ return false;
+ }
+
+ ssize_t bytes = GetFileSize(fileName);
+ if (bytes <= 0) {
+ ALOGE("RetrieveHashedFile: invalid file size: %s", fileName.c_str());
+ // Remove the corrupted file so the caller will not get the same error
+ // when trying to access the file repeatedly, causing the system to stall.
+ RemoveFile(fileName);
+ return false;
+ }
+
+ std::string serializedHashFile;
+ serializedHashFile.resize(bytes);
+ bytes = mFileHandle.Read(fileName, &serializedHashFile);
+
+ if (bytes != static_cast<ssize_t>(serializedHashFile.size())) {
+ ALOGE("RetrieveHashedFile: Failed to read from %s", fileName.c_str());
+ ALOGV("RetrieveHashedFile: expected: %zd, actual: %zd", serializedHashFile.size(), bytes);
+ // Remove the corrupted file so the caller will not get the same error
+ // when trying to access the file repeatedly, causing the system to stall.
+ RemoveFile(fileName);
+ return false;
+ }
+
+ ALOGV("RetrieveHashedFile: read %zd from %s", bytes, fileName.c_str());
+
+ HashedFile hashFile;
+ if (!hashFile.ParseFromString(serializedHashFile)) {
+ ALOGE("RetrieveHashedFile: Unable to parse hash file");
+ // Remove corrupt file.
+ RemoveFile(fileName);
+ return false;
+ }
+
+ std::string hash;
+ if (!Hash(hashFile.file(), &hash)) {
+ ALOGE("RetrieveHashedFile: Hash computation failed");
+ return false;
+ }
+
+ if (hash != hashFile.hash()) {
+ ALOGE("RetrieveHashedFile: Hash mismatch");
+ // Remove corrupt file.
+ RemoveFile(fileName);
+ return false;
+ }
+
+ if (!deSerializedFile->ParseFromString(hashFile.file())) {
+ ALOGE("RetrieveHashedFile: Unable to parse file");
+ // Remove corrupt file.
+ RemoveFile(fileName);
+ return false;
+ }
+
+ return true;
+}
+
+bool DeviceFiles::FileExists(const std::string& fileName) const {
+ return mFileHandle.FileExists(fileName);
+}
+
+bool DeviceFiles::RemoveFile(const std::string& fileName) {
+ return mFileHandle.RemoveFile(fileName);
+}
+
+ssize_t DeviceFiles::GetFileSize(const std::string& fileName) const {
+ return mFileHandle.GetFileSize(fileName);
+}
+
+} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/common/InitDataParser.cpp b/drm/mediadrm/plugins/clearkey/common/InitDataParser.cpp
new file mode 100644
index 0000000..8fb4a15
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/common/InitDataParser.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2021 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
+#include "include/ClearKeyTypes.h"
+#define LOG_TAG "clearkey-InitDataParser"
+
+#include <algorithm>
+#include <arpa/inet.h>
+#include <utils/Log.h>
+
+#include "InitDataParser.h"
+
+#include "Base64.h"
+
+#include "ClearKeyUUID.h"
+#include "MimeType.h"
+
+namespace {
+const size_t kKeyIdSize = 16;
+const size_t kSystemIdSize = 16;
+} // namespace
+
+namespace clearkeydrm {
+
+std::vector<uint8_t> StrToVector(const std::string& str) {
+ std::vector<uint8_t> vec(str.begin(), str.end());
+ return vec;
+}
+
+CdmResponseType InitDataParser::parse(const std::vector<uint8_t>& initData,
+ const std::string& mimeType,
+ CdmKeyType keyType,
+ std::vector<uint8_t>* licenseRequest) {
+ // Build a list of the key IDs
+ std::vector<const uint8_t*> keyIds;
+
+ if (mimeType == kIsoBmffVideoMimeType.c_str() || mimeType == kIsoBmffAudioMimeType.c_str() ||
+ mimeType == kCencInitDataFormat.c_str()) {
+ auto res = parsePssh(initData, &keyIds);
+ if (res != clearkeydrm::OK) {
+ return res;
+ }
+ } else if (mimeType == kWebmVideoMimeType.c_str() || mimeType == kWebmAudioMimeType.c_str() ||
+ mimeType == kWebmInitDataFormat.c_str()) {
+ // WebM "init data" is just a single key ID
+ if (initData.size() != kKeyIdSize) {
+ return clearkeydrm::ERROR_CANNOT_HANDLE;
+ }
+ keyIds.push_back(initData.data());
+ } else {
+ return clearkeydrm::ERROR_CANNOT_HANDLE;
+ }
+
+ if (keyType == clearkeydrm::KEY_TYPE_RELEASE) {
+ // restore key
+ }
+
+ // Build the request
+ std::string requestJson = generateRequest(keyType, keyIds);
+ std::vector<uint8_t> requestJsonVec = StrToVector(requestJson);
+
+ licenseRequest->clear();
+ licenseRequest->insert(licenseRequest->end(), requestJsonVec.begin(), requestJsonVec.end());
+ return clearkeydrm::OK;
+}
+
+CdmResponseType InitDataParser::parsePssh(const std::vector<uint8_t>& initData,
+ std::vector<const uint8_t*>* keyIds) {
+ // Description of PSSH format:
+ // https://w3c.github.io/encrypted-media/format-registry/initdata/cenc.html
+ size_t readPosition = 0;
+
+ uint32_t expectedSize = initData.size();
+ const char psshIdentifier[4] = {'p', 's', 's', 'h'};
+ const uint8_t psshVersion1[4] = {1, 0, 0, 0};
+ uint32_t keyIdCount = 0;
+ size_t headerSize = sizeof(expectedSize) + sizeof(psshIdentifier) + sizeof(psshVersion1) +
+ kSystemIdSize + sizeof(keyIdCount);
+ if (initData.size() < headerSize) {
+ return clearkeydrm::ERROR_CANNOT_HANDLE;
+ }
+
+ // Validate size field
+ expectedSize = htonl(expectedSize);
+ if (memcmp(&initData[readPosition], &expectedSize, sizeof(expectedSize)) != 0) {
+ return clearkeydrm::ERROR_CANNOT_HANDLE;
+ }
+ readPosition += sizeof(expectedSize);
+
+ // Validate PSSH box identifier
+ if (memcmp(&initData[readPosition], psshIdentifier, sizeof(psshIdentifier)) != 0) {
+ return clearkeydrm::ERROR_CANNOT_HANDLE;
+ }
+ readPosition += sizeof(psshIdentifier);
+
+ // Validate EME version number
+ if (memcmp(&initData[readPosition], psshVersion1, sizeof(psshVersion1)) != 0) {
+ return clearkeydrm::ERROR_CANNOT_HANDLE;
+ }
+ readPosition += sizeof(psshVersion1);
+
+ // Validate system ID
+ if (!isClearKeyUUID(&initData[readPosition])) {
+ return clearkeydrm::ERROR_CANNOT_HANDLE;
+ }
+ readPosition += kSystemIdSize;
+
+ // Read key ID count
+ memcpy(&keyIdCount, &initData[readPosition], sizeof(keyIdCount));
+ keyIdCount = ntohl(keyIdCount);
+ readPosition += sizeof(keyIdCount);
+
+ uint64_t psshSize = 0;
+ if (__builtin_mul_overflow(keyIdCount, kKeyIdSize, &psshSize) ||
+ __builtin_add_overflow(readPosition, psshSize, &psshSize) ||
+ psshSize != initData.size() - sizeof(uint32_t) /* DataSize(0) */) {
+ return clearkeydrm::ERROR_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 clearkeydrm::OK;
+}
+
+std::string InitDataParser::generateRequest(CdmKeyType keyType,
+ const std::vector<const uint8_t*>& keyIds) {
+ const std::string kRequestPrefix("{\"kids\":[");
+ const std::string kTemporarySession("],\"type\":\"temporary\"}");
+ const std::string kPersistentSession("],\"type\":\"persistent-license\"}");
+
+ 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('\"');
+ }
+ if (keyType == clearkeydrm::KEY_TYPE_STREAMING) {
+ request.append(kTemporarySession);
+ } else if (keyType == clearkeydrm::KEY_TYPE_OFFLINE ||
+ keyType == clearkeydrm::KEY_TYPE_RELEASE) {
+ request.append(kPersistentSession);
+ }
+
+ // 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 clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/common/JsonWebKey.cpp b/drm/mediadrm/plugins/clearkey/common/JsonWebKey.cpp
new file mode 100644
index 0000000..ddbc594
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/common/JsonWebKey.cpp
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2021 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 "clearkey-JsonWebKey"
+
+#include <utils/Log.h>
+
+#include "JsonWebKey.h"
+
+#include "Base64.h"
+
+namespace {
+const std::string kBase64Padding("=");
+const std::string kKeysTag("keys");
+const std::string kKeyTypeTag("kty");
+const std::string kKeyTag("k");
+const std::string kKeyIdTag("kid");
+const std::string kMediaSessionType("type");
+const std::string kPersistentLicenseSession("persistent-license");
+const std::string kSymmetricKeyValue("oct");
+const std::string kTemporaryLicenseSession("temporary");
+} // namespace
+
+namespace clearkeydrm {
+
+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 (mJsonObjects.size() == 0 || !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);
+ }
+ }
+
+ ::android::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 clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/common/MemoryFileSystem.cpp b/drm/mediadrm/plugins/clearkey/common/MemoryFileSystem.cpp
new file mode 100644
index 0000000..1045458
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/common/MemoryFileSystem.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2021 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 <utils/Log.h>
+#include <string>
+#include <vector>
+
+#include "MemoryFileSystem.h"
+
+namespace clearkeydrm {
+
+std::string MemoryFileSystem::GetFileName(const std::string& path) {
+ size_t index = path.find_last_of('/');
+ if (index != std::string::npos) {
+ return path.substr(index + 1);
+ } else {
+ return path;
+ }
+}
+
+bool MemoryFileSystem::FileExists(const std::string& fileName) const {
+ auto result = mMemoryFileSystem.find(fileName);
+ return result != mMemoryFileSystem.end();
+}
+
+ssize_t MemoryFileSystem::GetFileSize(const std::string& fileName) const {
+ auto result = mMemoryFileSystem.find(fileName);
+ if (result != mMemoryFileSystem.end()) {
+ return static_cast<ssize_t>(result->second.getFileSize());
+ } else {
+ ALOGE("Failed to get size for %s", fileName.c_str());
+ return -1;
+ }
+}
+
+std::vector<std::string> MemoryFileSystem::ListFiles() const {
+ std::vector<std::string> list;
+ for (const auto& filename : mMemoryFileSystem) {
+ list.push_back(filename.first);
+ }
+ return list;
+}
+
+size_t MemoryFileSystem::Read(const std::string& path, std::string* buffer) {
+ std::string key = GetFileName(path);
+ auto result = mMemoryFileSystem.find(key);
+ if (result != mMemoryFileSystem.end()) {
+ std::string serializedHashFile = result->second.getContent();
+ buffer->assign(serializedHashFile);
+ return buffer->size();
+ } else {
+ ALOGE("Failed to read from %s", path.c_str());
+ return -1;
+ }
+}
+
+size_t MemoryFileSystem::Write(const std::string& path, const MemoryFile& memoryFile) {
+ std::string key = GetFileName(path);
+ auto result = mMemoryFileSystem.find(key);
+ if (result != mMemoryFileSystem.end()) {
+ mMemoryFileSystem.erase(key);
+ }
+ mMemoryFileSystem.insert(std::pair<std::string, MemoryFile>(key, memoryFile));
+ return memoryFile.getFileSize();
+}
+
+bool MemoryFileSystem::RemoveFile(const std::string& fileName) {
+ auto result = mMemoryFileSystem.find(fileName);
+ if (result != mMemoryFileSystem.end()) {
+ mMemoryFileSystem.erase(result);
+ return true;
+ } else {
+ ALOGE("Cannot find license to remove: %s", fileName.c_str());
+ return false;
+ }
+}
+
+bool MemoryFileSystem::RemoveAllFiles() {
+ mMemoryFileSystem.clear();
+ return mMemoryFileSystem.empty();
+}
+
+} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/common/Session.cpp b/drm/mediadrm/plugins/clearkey/common/Session.cpp
new file mode 100644
index 0000000..d7fd13a
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/common/Session.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2021 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 "clearkey-Session"
+
+#include <utils/Log.h>
+
+#include "Session.h"
+
+#include "AesCtrDecryptor.h"
+#include "InitDataParser.h"
+#include "JsonWebKey.h"
+
+namespace clearkeydrm {
+
+using ::android::Mutex;
+using ::android::sp;
+
+CdmResponseType Session::getKeyRequest(const std::vector<uint8_t>& initData,
+ const std::string& mimeType,
+ CdmKeyType keyType,
+ std::vector<uint8_t>* keyRequest) const {
+ InitDataParser parser;
+ return parser.parse(initData, mimeType, keyType, keyRequest);
+}
+
+CdmResponseType 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 clearkeydrm::OK;
+ } else {
+ return clearkeydrm::ERROR_UNKNOWN;
+ }
+}
+
+CdmResponseType Session::decrypt(const KeyId keyId, const Iv iv,
+ const uint8_t* srcPtr, uint8_t* destPtr,
+ const std::vector<int32_t>& clearDataLengths,
+ const std::vector<int32_t>& encryptedDataLengths,
+ size_t* bytesDecryptedOut) {
+ Mutex::Autolock lock(mMapLock);
+
+ if (getMockError() != clearkeydrm::OK) {
+ return getMockError();
+ }
+
+ 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 clearkeydrm::ERROR_NO_LICENSE;
+ }
+
+ clearkeydrm::AesCtrDecryptor decryptor;
+ auto status = decryptor.decrypt(itr->second /*key*/, iv, srcPtr, destPtr,
+ clearDataLengths,
+ encryptedDataLengths,
+ bytesDecryptedOut);
+ return status;
+}
+
+} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/common/SessionLibrary.cpp b/drm/mediadrm/plugins/clearkey/common/SessionLibrary.cpp
new file mode 100644
index 0000000..6b2ff38
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/common/SessionLibrary.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2021 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 "clearkey-SessionLibrary"
+
+#include <utils/Log.h>
+
+#include "SessionLibrary.h"
+
+namespace clearkeydrm {
+
+using ::android::Mutex;
+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 clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/common/include/AesCtrDecryptor.h b/drm/mediadrm/plugins/clearkey/common/include/AesCtrDecryptor.h
new file mode 100644
index 0000000..dbf3098
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/common/include/AesCtrDecryptor.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+#pragma once
+
+#include <cstdint>
+
+#include "ClearKeyTypes.h"
+
+namespace clearkeydrm {
+
+class AesCtrDecryptor {
+ public:
+ AesCtrDecryptor() {}
+
+ CdmResponseType decrypt(const std::vector<uint8_t>& key, const Iv iv, const uint8_t* source,
+ uint8_t* destination,
+ const std::vector<int32_t>& clearDataLengths,
+ const std::vector<int32_t>& encryptedDataLengths,
+ size_t* bytesDecryptedOut);
+
+ private:
+ CLEARKEY_DISALLOW_COPY_AND_ASSIGN(AesCtrDecryptor);
+};
+
+} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/common/include/Base64.h b/drm/mediadrm/plugins/clearkey/common/include/Base64.h
new file mode 100644
index 0000000..075d247
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/common/include/Base64.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+#pragma once
+
+#include "Buffer.h"
+
+namespace clearkeydrm {
+
+struct Buffer;
+
+::android::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 clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/common/include/Buffer.h b/drm/mediadrm/plugins/clearkey/common/include/Buffer.h
new file mode 100644
index 0000000..d41c4f3
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/common/include/Buffer.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+#pragma once
+
+#include <utils/RefBase.h>
+
+#include "ClearKeyTypes.h"
+
+namespace clearkeydrm {
+struct Buffer : public ::android::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 clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/common/include/ClearKeyDrmProperties.h b/drm/mediadrm/plugins/clearkey/common/include/ClearKeyDrmProperties.h
new file mode 100644
index 0000000..9a22633
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/common/include/ClearKeyDrmProperties.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+#pragma once
+
+#include <string>
+
+namespace clearkeydrm {
+static const std::string kVendorKey("vendor");
+static const std::string kVendorValue("Google");
+static const std::string kVersionKey("version");
+static const std::string kVersionValue("1.2");
+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 kDrmErrorTestKey("drmErrorTest");
+static const std::string kDrmErrorTestValue("");
+static const std::string kResourceContentionValue("resourceContention");
+static const std::string kLostStateValue("lostState");
+static const std::string kFrameTooLargeValue("frameTooLarge");
+static const std::string kInvalidStateValue("invalidState");
+
+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};
+
+// settable byte array property
+static const std::string kClientIdKey("clientId");
+
+// TODO stub out metrics for nw
+static const std::string kMetricsKey("metrics");
+static const uint8_t kMetricsData[] = {0};
+
+} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/common/include/ClearKeyTypes.h b/drm/mediadrm/plugins/clearkey/common/include/ClearKeyTypes.h
new file mode 100644
index 0000000..0cc9511
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/common/include/ClearKeyTypes.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <map>
+#include <vector>
+
+namespace clearkeydrm {
+
+const uint8_t kBlockSize = 16; // AES_BLOCK_SIZE;
+typedef uint8_t KeyId[kBlockSize];
+typedef uint8_t Iv[kBlockSize];
+
+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;
+
+enum CdmResponseType : int32_t {
+ OK = 0,
+ ERROR_NO_LICENSE = 1,
+ ERROR_SESSION_NOT_OPENED = 3,
+ ERROR_CANNOT_HANDLE = 4,
+ ERROR_INVALID_STATE = 5,
+ BAD_VALUE = 6,
+ ERROR_DECRYPT = 11,
+ ERROR_UNKNOWN = 12,
+ ERROR_INSUFFICIENT_SECURITY = 13,
+ ERROR_FRAME_TOO_LARGE = 14,
+ ERROR_SESSION_LOST_STATE = 15,
+ ERROR_RESOURCE_CONTENTION = 16,
+};
+
+enum CdmKeyType : int32_t {
+ KEY_TYPE_OFFLINE = 0,
+ KEY_TYPE_STREAMING = 1,
+ KEY_TYPE_RELEASE = 2,
+};
+
+} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/common/include/ClearKeyUUID.h b/drm/mediadrm/plugins/clearkey/common/include/ClearKeyUUID.h
index fe10fba..8911024 100644
--- a/drm/mediadrm/plugins/clearkey/common/include/ClearKeyUUID.h
+++ b/drm/mediadrm/plugins/clearkey/common/include/ClearKeyUUID.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -13,9 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-#ifndef CLEARKEY_UUID_H_
-#define CLEARKEY_UUID_H_
+#pragma once
#include <array>
#include <cstdint>
@@ -27,6 +25,4 @@
std::vector<std::array<uint8_t, 16>> getSupportedCryptoSchemes();
-} // namespace clearkeydrm
-
-#endif // CLEARKEY_UUID_H_
+} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/common/include/DeviceFiles.h b/drm/mediadrm/plugins/clearkey/common/include/DeviceFiles.h
new file mode 100644
index 0000000..5698441
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/common/include/DeviceFiles.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+#pragma once
+
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "ClearKeyTypes.h"
+#include "MemoryFileSystem.h"
+
+namespace clearkeydrm {
+class OfflineFile;
+class DeviceFiles {
+ public:
+ typedef enum {
+ kLicenseStateUnknown,
+ kLicenseStateActive,
+ kLicenseStateReleasing,
+ } LicenseState;
+
+ DeviceFiles(){};
+ virtual ~DeviceFiles(){};
+
+ virtual bool StoreLicense(const std::string& keySetId, LicenseState state,
+ const std::string& keyResponse);
+
+ virtual bool RetrieveLicense(const std::string& key_set_id, LicenseState* state,
+ std::string* offlineLicense);
+
+ virtual bool LicenseExists(const std::string& keySetId);
+
+ virtual std::vector<std::string> ListLicenses() const;
+
+ virtual bool DeleteLicense(const std::string& keySetId);
+
+ virtual bool DeleteAllLicenses();
+
+ private:
+ bool FileExists(const std::string& path) const;
+ ssize_t GetFileSize(const std::string& fileName) const;
+ bool RemoveFile(const std::string& fileName);
+
+ bool RetrieveHashedFile(
+ const std::string& fileName,
+ OfflineFile* deSerializedFile);
+ bool StoreFileRaw(const std::string& fileName, const std::string& serializedFile);
+ bool StoreFileWithHash(const std::string& fileName, const std::string& serializedFile);
+
+ MemoryFileSystem mFileHandle;
+
+ CLEARKEY_DISALLOW_COPY_AND_ASSIGN(DeviceFiles);
+};
+
+} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/common/include/InitDataParser.h b/drm/mediadrm/plugins/clearkey/common/include/InitDataParser.h
new file mode 100644
index 0000000..8ecc8e3
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/common/include/InitDataParser.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+#pragma once
+
+#include "ClearKeyTypes.h"
+
+namespace clearkeydrm {
+
+class InitDataParser {
+ public:
+ InitDataParser() {}
+
+ CdmResponseType parse(const std::vector<uint8_t>& initData, const std::string& mimeType,
+ CdmKeyType keyType, std::vector<uint8_t>* licenseRequest);
+
+ private:
+ CLEARKEY_DISALLOW_COPY_AND_ASSIGN(InitDataParser);
+
+ CdmResponseType parsePssh(const std::vector<uint8_t>& initData,
+ std::vector<const uint8_t*>* keyIds);
+
+ std::string generateRequest(CdmKeyType keyType, const std::vector<const uint8_t*>& keyIds);
+};
+
+} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/common/include/JsonWebKey.h b/drm/mediadrm/plugins/clearkey/common/include/JsonWebKey.h
new file mode 100644
index 0000000..6681553
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/common/include/JsonWebKey.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "jsmn.h"
+#include "ClearKeyTypes.h"
+
+namespace clearkeydrm {
+
+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 clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/common/include/MemoryFileSystem.h b/drm/mediadrm/plugins/clearkey/common/include/MemoryFileSystem.h
new file mode 100644
index 0000000..5642a0f
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/common/include/MemoryFileSystem.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+#pragma once
+
+#include <map>
+#include <string>
+
+#include "ClearKeyTypes.h"
+
+namespace clearkeydrm {
+
+// Using android file system requires clearkey plugin to update
+// its sepolicy. However, we are unable to update sepolicy for
+// older vendor partitions. To provide backward compatibility,
+// clearkey plugin implements a very simple file system in memory.
+// This memory file system does not support directory structure.
+class MemoryFileSystem {
+ public:
+ struct MemoryFile {
+ std::string fileName; // excludes path
+ std::string content;
+ size_t fileSize;
+
+ std::string getContent() const { return content; }
+ size_t getFileSize() const { return fileSize; }
+ void setContent(const std::string& file) { content = file; }
+ void setFileName(const std::string& name) { fileName = name; }
+ void setFileSize(size_t size) {
+ content.resize(size);
+ fileSize = size;
+ }
+ };
+
+ MemoryFileSystem(){};
+ virtual ~MemoryFileSystem(){};
+
+ bool FileExists(const std::string& fileName) const;
+ ssize_t GetFileSize(const std::string& fileName) const;
+ std::vector<std::string> ListFiles() const;
+ size_t Read(const std::string& pathName, std::string* buffer);
+ bool RemoveAllFiles();
+ bool RemoveFile(const std::string& fileName);
+ size_t Write(const std::string& pathName, const MemoryFile& memoryFile);
+
+ private:
+ // License file name is made up of a unique keySetId, therefore,
+ // the filename can be used as the key to locate licenses in the
+ // memory file system.
+ std::map<std::string, MemoryFile> mMemoryFileSystem;
+
+ std::string GetFileName(const std::string& path);
+
+ CLEARKEY_DISALLOW_COPY_AND_ASSIGN(MemoryFileSystem);
+};
+
+} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/common/include/MimeTypeStdStr.h b/drm/mediadrm/plugins/clearkey/common/include/MimeTypeStdStr.h
new file mode 100644
index 0000000..dea2974
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/common/include/MimeTypeStdStr.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+#pragma once
+
+#include <string>
+
+namespace {
+const std::string kCencInitDataFormat("cenc");
+const std::string kIsoBmffAudioMimeType("audio/mp4");
+const std::string kIsoBmffVideoMimeType("video/mp4");
+const std::string kWebmInitDataFormat("webm");
+const std::string kWebmAudioMimeType("audio/webm");
+const std::string kWebmVideoMimeType("video/webm");
+} // namespace
diff --git a/drm/mediadrm/plugins/clearkey/common/include/Session.h b/drm/mediadrm/plugins/clearkey/common/include/Session.h
new file mode 100644
index 0000000..e2d4e32
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/common/include/Session.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+#pragma once
+
+#include <utils/Mutex.h>
+#include <utils/RefBase.h>
+
+#include <cstdint>
+#include <vector>
+
+#include "ClearKeyTypes.h"
+
+namespace clearkeydrm {
+
+class Session : public ::android::RefBase {
+ public:
+ explicit Session(const std::vector<uint8_t>& sessionId)
+ : mSessionId(sessionId), mMockError(clearkeydrm::OK) {}
+ virtual ~Session() {}
+
+ const std::vector<uint8_t>& sessionId() const { return mSessionId; }
+
+ CdmResponseType getKeyRequest(const std::vector<uint8_t>& initDataType,
+ const std::string& mimeType,
+ CdmKeyType keyType,
+ std::vector<uint8_t>* keyRequest) const;
+
+ CdmResponseType provideKeyResponse(const std::vector<uint8_t>& response);
+
+ CdmResponseType decrypt(const KeyId keyId, const Iv iv, const uint8_t* srcPtr, uint8_t* dstPtr,
+ const std::vector<int32_t>& clearDataLengths,
+ const std::vector<int32_t>& encryptedDataLengths,
+ size_t* bytesDecryptedOut);
+
+ void setMockError(CdmResponseType error) { mMockError = error; }
+ CdmResponseType getMockError() const { return mMockError; }
+
+ private:
+ CLEARKEY_DISALLOW_COPY_AND_ASSIGN(Session);
+
+ const std::vector<uint8_t> mSessionId;
+ KeyMap mKeyMap;
+ ::android::Mutex mMapLock;
+
+ // For mocking error return scenarios
+ CdmResponseType mMockError;
+};
+
+} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/common/include/SessionLibrary.h b/drm/mediadrm/plugins/clearkey/common/include/SessionLibrary.h
new file mode 100644
index 0000000..987e328
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/common/include/SessionLibrary.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+#pragma once
+
+#include <utils/Mutex.h>
+#include <utils/RefBase.h>
+
+#include "ClearKeyTypes.h"
+#include "Session.h"
+
+namespace clearkeydrm {
+
+class SessionLibrary : public ::android::RefBase {
+ public:
+ static SessionLibrary* get();
+
+ ::android::sp<Session> createSession();
+
+ ::android::sp<Session> findSession(const std::vector<uint8_t>& sessionId);
+
+ void destroySession(const ::android::sp<Session>& session);
+
+ size_t numOpenSessions() const { return mSessions.size(); }
+
+ private:
+ CLEARKEY_DISALLOW_COPY_AND_ASSIGN(SessionLibrary);
+
+ SessionLibrary() : mNextSessionId(1) {}
+
+ static ::android::Mutex sSingletonLock;
+ static SessionLibrary* sSingleton;
+
+ ::android::Mutex mSessionsLock;
+ uint32_t mNextSessionId;
+ std::map<std::vector<uint8_t>, ::android::sp<Session>> mSessions;
+};
+
+} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/common/protos/DeviceFiles.proto b/drm/mediadrm/plugins/clearkey/common/protos/DeviceFiles.proto
new file mode 100644
index 0000000..2d98656
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/common/protos/DeviceFiles.proto
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+syntax = "proto2";
+
+package clearkeydrm;
+
+// need this if we are using libprotobuf-cpp-2.3.0-lite
+option optimize_for = LITE_RUNTIME;
+
+message License {
+ enum LicenseState {
+ ACTIVE = 1;
+ RELEASING = 2;
+ }
+
+ optional LicenseState state = 1;
+ optional bytes license = 2;
+}
+
+message OfflineFile {
+ enum FileType {
+ LICENSE = 1;
+ }
+
+ enum FileVersion {
+ VERSION_1 = 1;
+ }
+
+ optional FileType type = 1;
+ optional FileVersion version = 2 [default = VERSION_1];
+ optional License license = 3;
+
+}
+
+message HashedFile {
+ optional bytes file = 1;
+ // A raw (not hex-encoded) SHA256, taken over the bytes of 'file'.
+ optional bytes hash = 2;
+}