diff options
Diffstat (limited to 'drm')
99 files changed, 5648 insertions, 421 deletions
diff --git a/drm/common/Android.bp b/drm/common/Android.bp index 0098c89630..1552c3f340 100644 --- a/drm/common/Android.bp +++ b/drm/common/Android.bp @@ -33,6 +33,8 @@ cc_library_static { "ReadWriteUtils.cpp", ], + cflags: ["-Wall", "-Werror"], + static_libs: ["libbinder"], export_include_dirs: ["include"], diff --git a/drm/libdrmframework/plugins/common/util/Android.bp b/drm/libdrmframework/plugins/common/util/Android.bp index 0c0b6f26fa..7372eb774e 100644 --- a/drm/libdrmframework/plugins/common/util/Android.bp +++ b/drm/libdrmframework/plugins/common/util/Android.bp @@ -19,5 +19,7 @@ cc_library_static { srcs: ["src/MimeTypeUtil.cpp"], + cflags: ["-Wall", "-Werror"], + export_include_dirs: ["include"], } diff --git a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.bp b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.bp index 3f0f5f78e0..28a78aa21a 100644 --- a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.bp +++ b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.bp @@ -21,6 +21,9 @@ cc_library_shared { "-DUSE_64BIT_DRM_API", // The flag below turns on local debug printouts //"-DDRM_OMA_FL_ENGINE_DEBUG", + "-Wall", + "-Werror", + "-Wno-unused-variable", ], srcs: ["src/FwdLockEngine.cpp"], diff --git a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp index 830def931c..73eea89249 100644 --- a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp +++ b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp @@ -502,8 +502,8 @@ status_t FwdLockEngine::onOpenDecryptSession(int /* uniqueId */, int retVal = FwdLockFile_CheckHeaderIntegrity(fileDesc); DecodeSession* decodeSession = new DecodeSession(fileDesc); - if (retVal && NULL != decodeSession) { - decodeSessionMap.addValue(decryptHandle->decryptId, decodeSession); + if (retVal && NULL != decodeSession && + decodeSessionMap.addValue(decryptHandle->decryptId, decodeSession)) { const char *pmime= FwdLockFile_GetContentType(fileDesc); String8 contentType = String8(pmime == NULL ? "" : pmime); contentType.toLower(); @@ -513,7 +513,11 @@ status_t FwdLockEngine::onOpenDecryptSession(int /* uniqueId */, decryptHandle->decryptInfo = NULL; result = DRM_NO_ERROR; } else { - LOG_VERBOSE("FwdLockEngine::onOpenDecryptSession Integrity Check failed for the fd"); + if (retVal && NULL != decodeSession) { + LOG_VERBOSE("FwdLockEngine::onOpenDecryptSession Integrity Check failed for the fd"); + } else { + LOG_VERBOSE("FwdLockEngine::onOpenDecryptSession DecodeSesssion insertion failed"); + } FwdLockFile_detach(fileDesc); delete decodeSession; } @@ -631,7 +635,7 @@ ssize_t FwdLockEngine::onRead(int /* uniqueId */, ssize_t size = -1; if (NULL != decryptHandle && - decodeSessionMap.isCreated(decryptHandle->decryptId) && + decodeSessionMap.isCreated(decryptHandle->decryptId) && NULL != buffer && numBytes > -1) { DecodeSession* session = decodeSessionMap.getValue(decryptHandle->decryptId); diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/common/Android.bp b/drm/libdrmframework/plugins/forward-lock/internal-format/common/Android.bp index 698f278058..3be327ad95 100644 --- a/drm/libdrmframework/plugins/forward-lock/internal-format/common/Android.bp +++ b/drm/libdrmframework/plugins/forward-lock/internal-format/common/Android.bp @@ -19,6 +19,8 @@ cc_library_static { srcs: ["FwdLockGlue.c"], + cflags: ["-Wall", "-Werror"], + shared_libs: ["libcrypto"], export_include_dirs: ["."], diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/converter/Android.bp b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/Android.bp index 33f2fe0702..d4e04b8218 100644 --- a/drm/libdrmframework/plugins/forward-lock/internal-format/converter/Android.bp +++ b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/Android.bp @@ -19,6 +19,8 @@ cc_library_static { srcs: ["FwdLockConv.c"], + cflags: ["-Wall", "-Werror"], + shared_libs: ["libcrypto"], static_libs: ["libfwdlock-common"], diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/Android.bp b/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/Android.bp index b6d7a06e84..0bf2737c6f 100644 --- a/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/Android.bp +++ b/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/Android.bp @@ -19,6 +19,8 @@ cc_library_static { srcs: ["FwdLockFile.c"], + cflags: ["-Wall", "-Werror"], + shared_libs: ["libcrypto"], static_libs: ["libfwdlock-common"], diff --git a/drm/libdrmframework/plugins/passthru/Android.bp b/drm/libdrmframework/plugins/passthru/Android.bp index 1dcf89cc07..05b6440a0b 100644 --- a/drm/libdrmframework/plugins/passthru/Android.bp +++ b/drm/libdrmframework/plugins/passthru/Android.bp @@ -32,5 +32,7 @@ cc_library_shared { cflags: [ // Set the following flag to enable the decryption passthru flow //"-DENABLE_PASSTHRU_DECRYPTION", + "-Wall", + "-Werror", ], } diff --git a/drm/libmediadrm/Android.bp b/drm/libmediadrm/Android.bp index f90656434d..4991e50e8e 100644 --- a/drm/libmediadrm/Android.bp +++ b/drm/libmediadrm/Android.bp @@ -2,10 +2,11 @@ // libmediadrm // -cc_library_shared { +// TODO: change it back to cc_library_shared when MediaPlayer2 switches to +// using NdkMediaDrm, instead of MediaDrm.java. +cc_library { name: "libmediadrm", - srcs: [ "DrmPluginPath.cpp", "DrmSessionManager.cpp", @@ -13,29 +14,26 @@ cc_library_shared { "IDrm.cpp", "IDrmClient.cpp", "IMediaDrmService.cpp", - "PluginMetricsReporting.cpp", "SharedLibrary.cpp", "DrmHal.cpp", "CryptoHal.cpp", - "protos/plugin_metrics.proto", ], - proto: { - type: "lite", - }, - shared_libs: [ "libbinder", "libcutils", "libdl", "liblog", + "libmediadrmmetrics_lite", "libmediametrics", "libmediautils", + "libprotobuf-cpp-lite", "libstagefright_foundation", "libutils", "android.hardware.drm@1.0", + "android.hardware.drm@1.1", + "libhidlallocatorutils", "libhidlbase", - "libhidlmemory", "libhidltransport", ], @@ -44,3 +42,70 @@ cc_library_shared { "-Wall", ], } + +// This is the version of the drm metrics configured for protobuf lite. +cc_library_shared { + name: "libmediadrmmetrics_lite", + srcs: [ + "DrmMetrics.cpp", + "PluginMetricsReporting.cpp", + "protos/metrics.proto", + ], + + proto: { + export_proto_headers: true, + type: "lite", + }, + shared_libs: [ + "android.hardware.drm@1.0", + "android.hardware.drm@1.1", + "libbase", + "libbinder", + "libhidlbase", + "liblog", + "libmediametrics", + "libprotobuf-cpp-lite", + "libstagefright_foundation", + "libutils", + ], + cflags: [ + // Suppress unused parameter and no error options. These cause problems + // with the when using the map type in a proto definition. + "-Wno-unused-parameter", + "-Wno-error", + ], +} + +// This is the version of the drm metrics library configured for full protobuf. +cc_library_shared { + name: "libmediadrmmetrics_full", + srcs: [ + "DrmMetrics.cpp", + "PluginMetricsReporting.cpp", + "protos/metrics.proto", + ], + + proto: { + export_proto_headers: true, + type: "full", + }, + shared_libs: [ + "android.hardware.drm@1.0", + "android.hardware.drm@1.1", + "libbase", + "libbinder", + "libhidlbase", + "liblog", + "libmediametrics", + "libprotobuf-cpp-full", + "libstagefright_foundation", + "libutils", + ], + cflags: [ + // Suppress unused parameter and no error options. These cause problems + // when using the map type in a proto definition. + "-Wno-unused-parameter", + "-Wno-error", + ], +} + diff --git a/drm/libmediadrm/CryptoHal.cpp b/drm/libmediadrm/CryptoHal.cpp index 5dd25639ca..3035c5a250 100644 --- a/drm/libmediadrm/CryptoHal.cpp +++ b/drm/libmediadrm/CryptoHal.cpp @@ -22,13 +22,14 @@ #include <android/hidl/manager/1.0/IServiceManager.h> #include <binder/IMemory.h> -#include <cutils/native_handle.h> -#include <media/CryptoHal.h> +#include <hidlmemory/FrameworkUtils.h> #include <media/hardware/CryptoAPI.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AString.h> #include <media/stagefright/foundation/hexdump.h> #include <media/stagefright/MediaErrors.h> +#include <mediadrm/CryptoHal.h> + using ::android::hardware::drm::V1_0::BufferType; using ::android::hardware::drm::V1_0::DestinationBuffer; @@ -117,15 +118,24 @@ Vector<sp<ICryptoFactory>> CryptoHal::makeCryptoFactories() { auto manager = ::IServiceManager::getService(); if (manager != NULL) { - manager->listByInterface(ICryptoFactory::descriptor, + manager->listByInterface(drm::V1_0::ICryptoFactory::descriptor, + [&factories](const hidl_vec<hidl_string> ®istered) { + for (const auto &instance : registered) { + auto factory = drm::V1_0::ICryptoFactory::getService(instance); + if (factory != NULL) { + ALOGD("found drm@1.0 ICryptoFactory %s", instance.c_str()); + factories.push_back(factory); + } + } + } + ); + manager->listByInterface(drm::V1_1::ICryptoFactory::descriptor, [&factories](const hidl_vec<hidl_string> ®istered) { for (const auto &instance : registered) { - auto factory = ICryptoFactory::getService(instance); + auto factory = drm::V1_1::ICryptoFactory::getService(instance); if (factory != NULL) { + ALOGD("found drm@1.1 ICryptoFactory %s", instance.c_str()); factories.push_back(factory); - ALOGI("makeCryptoFactories: factory instance %s is %s", - instance.c_str(), - factory->isRemote() ? "Remote" : "Not Remote"); } } } @@ -136,7 +146,7 @@ Vector<sp<ICryptoFactory>> CryptoHal::makeCryptoFactories() { // must be in passthrough mode, load the default passthrough service auto passthrough = ICryptoFactory::getService(); if (passthrough != NULL) { - ALOGI("makeCryptoFactories: using default crypto instance"); + ALOGI("makeCryptoFactories: using default passthrough crypto instance"); factories.push_back(passthrough); } else { ALOGE("Failed to find any crypto factories"); @@ -213,10 +223,14 @@ bool CryptoHal::requiresSecureDecoderComponent(const char *mime) const { Mutex::Autolock autoLock(mLock); if (mInitCheck != OK) { - return mInitCheck; + return false; } - return mPlugin->requiresSecureDecoderComponent(hidl_string(mime)); + Return<bool> hResult = mPlugin->requiresSecureDecoderComponent(hidl_string(mime)); + if (!hResult.isOk()) { + return false; + } + return hResult; } @@ -227,26 +241,20 @@ bool CryptoHal::requiresSecureDecoderComponent(const char *mime) const { * are sent by providing an offset into the heap and a buffer size. */ int32_t CryptoHal::setHeapBase(const sp<IMemoryHeap>& heap) { + using ::android::hardware::fromHeap; + using ::android::hardware::HidlMemory; + if (heap == NULL) { ALOGE("setHeapBase(): heap is NULL"); return -1; } - native_handle_t* nativeHandle = native_handle_create(1, 0); - if (!nativeHandle) { - ALOGE("setHeapBase(), failed to create native handle"); - return -1; - } Mutex::Autolock autoLock(mLock); int32_t seqNum = mHeapSeqNum++; - - int fd = heap->getHeapID(); - nativeHandle->data[0] = fd; - auto hidlHandle = hidl_handle(nativeHandle); - auto hidlMemory = hidl_memory("ashmem", hidlHandle, heap->getSize()); + sp<HidlMemory> hidlMemory = fromHeap(heap); mHeapBases.add(seqNum, HeapBase(mNextBufferId, heap->getSize())); - Return<void> hResult = mPlugin->setSharedBufferBase(hidlMemory, mNextBufferId++); + Return<void> hResult = mPlugin->setSharedBufferBase(*hidlMemory, mNextBufferId++); ALOGE_IF(!hResult.isOk(), "setSharedBufferBase(): remote call failed"); return seqNum; } @@ -254,7 +262,22 @@ int32_t CryptoHal::setHeapBase(const sp<IMemoryHeap>& heap) { void CryptoHal::clearHeapBase(int32_t seqNum) { Mutex::Autolock autoLock(mLock); - mHeapBases.removeItem(seqNum); + /* + * Clear the remote shared memory mapping by setting the shared + * buffer base to a null hidl_memory. + * + * TODO: Add a releaseSharedBuffer method in a future DRM HAL + * API version to make this explicit. + */ + ssize_t index = mHeapBases.indexOfKey(seqNum); + if (index >= 0) { + if (mPlugin != NULL) { + uint32_t bufferId = mHeapBases[index].getBufferId(); + Return<void> hResult = mPlugin->setSharedBufferBase(hidl_memory(), bufferId); + ALOGE_IF(!hResult.isOk(), "setSharedBufferBase(): remote call failed"); + } + mHeapBases.removeItem(seqNum); + } } status_t CryptoHal::toSharedBuffer(const sp<IMemory>& memory, int32_t seqNum, ::SharedBuffer* buffer) { diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp index bc37557ed5..cf08610173 100644 --- a/drm/libmediadrm/DrmHal.cpp +++ b/drm/libmediadrm/DrmHal.cpp @@ -16,48 +16,81 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "DrmHal" +#include <iomanip> + #include <utils/Log.h> #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> -#include <android/hardware/drm/1.0/IDrmFactory.h> -#include <android/hardware/drm/1.0/IDrmPlugin.h> #include <android/hardware/drm/1.0/types.h> #include <android/hidl/manager/1.0/IServiceManager.h> #include <hidl/ServiceManagement.h> -#include <media/DrmHal.h> -#include <media/DrmSessionClientInterface.h> -#include <media/DrmSessionManager.h> +#include <media/EventMetric.h> #include <media/PluginMetricsReporting.h> #include <media/drm/DrmAPI.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AString.h> +#include <media/stagefright/foundation/base64.h> #include <media/stagefright/foundation/hexdump.h> #include <media/stagefright/MediaErrors.h> - -using ::android::hardware::drm::V1_0::EventType; -using ::android::hardware::drm::V1_0::IDrmFactory; -using ::android::hardware::drm::V1_0::IDrmPlugin; -using ::android::hardware::drm::V1_0::KeyedVector; -using ::android::hardware::drm::V1_0::KeyRequestType; -using ::android::hardware::drm::V1_0::KeyStatus; -using ::android::hardware::drm::V1_0::KeyStatusType; -using ::android::hardware::drm::V1_0::KeyType; -using ::android::hardware::drm::V1_0::KeyValue; -using ::android::hardware::drm::V1_0::SecureStop; -using ::android::hardware::drm::V1_0::Status; +#include <mediadrm/DrmHal.h> +#include <mediadrm/DrmSessionClientInterface.h> +#include <mediadrm/DrmSessionManager.h> + +using drm::V1_0::KeyedVector; +using drm::V1_0::KeyStatusType; +using drm::V1_0::KeyType; +using drm::V1_0::KeyValue; +using drm::V1_1::HdcpLevel;; +using drm::V1_0::SecureStop; +using drm::V1_1::SecureStopRelease; +using drm::V1_0::SecureStopId; +using drm::V1_1::SecurityLevel; +using drm::V1_0::Status; +using ::android::hardware::drm::V1_1::DrmMetricGroup; using ::android::hardware::hidl_array; using ::android::hardware::hidl_string; using ::android::hardware::hidl_vec; using ::android::hardware::Return; using ::android::hardware::Void; using ::android::hidl::manager::V1_0::IServiceManager; +using ::android::os::PersistableBundle; using ::android::sp; +namespace { + +// This constant corresponds to the PROPERTY_DEVICE_UNIQUE_ID constant +// in the MediaDrm API. +constexpr char kPropertyDeviceUniqueId[] = "deviceUniqueId"; +constexpr char kEqualsSign[] = "="; + +template<typename T> +std::string toBase64StringNoPad(const T* data, size_t size) { + // Note that the base 64 conversion only works with arrays of single-byte + // values. If the source is empty or is not an array of single-byte values, + // return empty string. + if (size == 0 || sizeof(data[0]) != 1) { + return ""; + } + + android::AString outputString; + encodeBase64(data, size, &outputString); + // Remove trailing equals padding if it exists. + while (outputString.size() > 0 && outputString.endsWith(kEqualsSign)) { + outputString.erase(outputString.size() - 1, 1); + } + + return std::string(outputString.c_str(), outputString.size()); +} + +} // anonymous namespace + namespace android { +#define INIT_CHECK() {if (mInitCheck != OK) return mInitCheck;} + static inline int getCallingPid() { return IPCThreadState::self()->getCallingPid(); } @@ -89,6 +122,42 @@ static hidl_string toHidlString(const String8& string) { return hidl_string(string.string()); } +static DrmPlugin::SecurityLevel toSecurityLevel(SecurityLevel level) { + switch(level) { + case SecurityLevel::SW_SECURE_CRYPTO: + return DrmPlugin::kSecurityLevelSwSecureCrypto; + case SecurityLevel::SW_SECURE_DECODE: + return DrmPlugin::kSecurityLevelSwSecureDecode; + case SecurityLevel::HW_SECURE_CRYPTO: + return DrmPlugin::kSecurityLevelHwSecureCrypto; + case SecurityLevel::HW_SECURE_DECODE: + return DrmPlugin::kSecurityLevelHwSecureDecode; + case SecurityLevel::HW_SECURE_ALL: + return DrmPlugin::kSecurityLevelHwSecureAll; + default: + return DrmPlugin::kSecurityLevelUnknown; + } +} + +static DrmPlugin::HdcpLevel toHdcpLevel(HdcpLevel level) { + switch(level) { + case HdcpLevel::HDCP_NONE: + return DrmPlugin::kHdcpNone; + case HdcpLevel::HDCP_V1: + return DrmPlugin::kHdcpV1; + case HdcpLevel::HDCP_V2: + return DrmPlugin::kHdcpV2; + case HdcpLevel::HDCP_V2_1: + return DrmPlugin::kHdcpV2_1; + case HdcpLevel::HDCP_V2_2: + return DrmPlugin::kHdcpV2_2; + case HdcpLevel::HDCP_NO_OUTPUT: + return DrmPlugin::kHdcpNoOutput; + default: + return DrmPlugin::kHdcpLevelUnknown; + } +} + static ::KeyedVector toHidlKeyedVector(const KeyedVector<String8, String8>& keyedVector) { @@ -121,6 +190,15 @@ static List<Vector<uint8_t>> toSecureStops(const hidl_vec<SecureStop>& return secureStops; } +static List<Vector<uint8_t>> toSecureStopIds(const hidl_vec<SecureStopId>& + hSecureStopIds) { + List<Vector<uint8_t>> secureStopIds; + for (size_t i = 0; i < hSecureStopIds.size(); i++) { + secureStopIds.push_back(toVector(hSecureStopIds[i])); + } + return secureStopIds; +} + static status_t toStatusT(Status status) { switch (status) { case Status::OK: @@ -196,35 +274,63 @@ DrmHal::DrmHal() } void DrmHal::closeOpenSessions() { - if (mPlugin != NULL) { - for (size_t i = 0; i < mOpenSessions.size(); i++) { - mPlugin->closeSession(toHidlVec(mOpenSessions[i])); - DrmSessionManager::Instance()->removeSession(mOpenSessions[i]); - } + Mutex::Autolock autoLock(mLock); + auto openSessions = mOpenSessions; + for (size_t i = 0; i < openSessions.size(); i++) { + mLock.unlock(); + closeSession(openSessions[i]); + mLock.lock(); } mOpenSessions.clear(); } DrmHal::~DrmHal() { - closeOpenSessions(); DrmSessionManager::Instance()->removeDrm(mDrmSessionClient); } +void DrmHal::cleanup() { + closeOpenSessions(); + + Mutex::Autolock autoLock(mLock); + reportPluginMetrics(); + reportFrameworkMetrics(); + + setListener(NULL); + mInitCheck = NO_INIT; + + if (mPlugin != NULL) { + if (!mPlugin->setListener(NULL).isOk()) { + mInitCheck = DEAD_OBJECT; + } + } + mPlugin.clear(); + mPluginV1_1.clear(); +} + Vector<sp<IDrmFactory>> DrmHal::makeDrmFactories() { Vector<sp<IDrmFactory>> factories; auto manager = hardware::defaultServiceManager(); if (manager != NULL) { - manager->listByInterface(IDrmFactory::descriptor, + manager->listByInterface(drm::V1_0::IDrmFactory::descriptor, + [&factories](const hidl_vec<hidl_string> ®istered) { + for (const auto &instance : registered) { + auto factory = drm::V1_0::IDrmFactory::getService(instance); + if (factory != NULL) { + ALOGD("found drm@1.0 IDrmFactory %s", instance.c_str()); + factories.push_back(factory); + } + } + } + ); + manager->listByInterface(drm::V1_1::IDrmFactory::descriptor, [&factories](const hidl_vec<hidl_string> ®istered) { for (const auto &instance : registered) { - auto factory = IDrmFactory::getService(instance); + auto factory = drm::V1_1::IDrmFactory::getService(instance); if (factory != NULL) { + ALOGD("found drm@1.1 IDrmFactory %s", instance.c_str()); factories.push_back(factory); - ALOGI("makeDrmFactories: factory instance %s is %s", - instance.c_str(), - factory->isRemote() ? "Remote" : "Not Remote"); } } } @@ -235,7 +341,7 @@ Vector<sp<IDrmFactory>> DrmHal::makeDrmFactories() { // must be in passthrough mode, load the default passthrough service auto passthrough = IDrmFactory::getService(); if (passthrough != NULL) { - ALOGI("makeDrmFactories: using default drm instance"); + ALOGI("makeDrmFactories: using default passthrough drm instance"); factories.push_back(passthrough); } else { ALOGE("Failed to find any drm factories"); @@ -246,6 +352,8 @@ Vector<sp<IDrmFactory>> DrmHal::makeDrmFactories() { sp<IDrmPlugin> DrmHal::makeDrmPlugin(const sp<IDrmFactory>& factory, const uint8_t uuid[16], const String8& appPackageName) { + mAppPackageName = appPackageName; + mMetrics.SetAppPackageName(appPackageName); sp<IDrmPlugin> plugin; Return<void> hResult = factory->createPlugin(uuid, appPackageName.string(), @@ -284,6 +392,7 @@ status_t DrmHal::setListener(const sp<IDrmClient>& listener) Return<void> DrmHal::sendEvent(EventType hEventType, const hidl_vec<uint8_t>& sessionId, const hidl_vec<uint8_t>& data) { + mMetrics.mEventCounter.Increment(hEventType); mEventLock.lock(); sp<IDrmClient> listener = mListener; @@ -374,12 +483,21 @@ Return<void> DrmHal::sendKeysChange(const hidl_vec<uint8_t>& sessionId, break; } obj.writeInt32(type); + mMetrics.mKeyStatusChangeCounter.Increment(keyStatus.type); } obj.writeInt32(hasNewUsableKey); Mutex::Autolock lock(mNotifyLock); listener->notify(DrmPlugin::kDrmPluginEventKeysChange, 0, &obj); + } else { + // There's no listener. But we still want to count the key change + // events. + size_t nKeys = keyStatusList.size(); + for (size_t i = 0; i < nKeys; i++) { + mMetrics.mKeyStatusChangeCounter.Increment(keyStatusList[i].type); + } } + return Void(); } @@ -407,6 +525,9 @@ status_t DrmHal::createPlugin(const uint8_t uuid[16], for (size_t i = 0; i < mFactories.size(); i++) { if (mFactories[i]->isCryptoSchemeSupported(uuid)) { mPlugin = makeDrmPlugin(mFactories[i], uuid, appPackageName); + if (mPlugin != NULL) { + mPluginV1_1 = drm::V1_1::IDrmPlugin::castFrom(mPlugin); + } } } @@ -424,46 +545,66 @@ status_t DrmHal::createPlugin(const uint8_t uuid[16], } status_t DrmHal::destroyPlugin() { - Mutex::Autolock autoLock(mLock); - if (mInitCheck != OK) { - return mInitCheck; - } - - closeOpenSessions(); - reportMetrics(); - setListener(NULL); - mInitCheck = NO_INIT; - - if (mPlugin != NULL) { - if (!mPlugin->setListener(NULL).isOk()) { - mInitCheck = DEAD_OBJECT; - } - } - mPlugin.clear(); + cleanup(); return OK; } -status_t DrmHal::openSession(Vector<uint8_t> &sessionId) { +status_t DrmHal::openSession(DrmPlugin::SecurityLevel level, + Vector<uint8_t> &sessionId) { Mutex::Autolock autoLock(mLock); + INIT_CHECK(); - if (mInitCheck != OK) { - return mInitCheck; + SecurityLevel hSecurityLevel; + bool setSecurityLevel = true; + + switch(level) { + case DrmPlugin::kSecurityLevelSwSecureCrypto: + hSecurityLevel = SecurityLevel::SW_SECURE_CRYPTO; + break; + case DrmPlugin::kSecurityLevelSwSecureDecode: + hSecurityLevel = SecurityLevel::SW_SECURE_DECODE; + break; + case DrmPlugin::kSecurityLevelHwSecureCrypto: + hSecurityLevel = SecurityLevel::HW_SECURE_CRYPTO; + break; + case DrmPlugin::kSecurityLevelHwSecureDecode: + hSecurityLevel = SecurityLevel::HW_SECURE_DECODE; + break; + case DrmPlugin::kSecurityLevelHwSecureAll: + hSecurityLevel = SecurityLevel::HW_SECURE_ALL; + break; + case DrmPlugin::kSecurityLevelMax: + setSecurityLevel = false; + break; + default: + return ERROR_DRM_CANNOT_HANDLE; } status_t err = UNKNOWN_ERROR; - bool retry = true; do { hidl_vec<uint8_t> hSessionId; - Return<void> hResult = mPlugin->openSession( - [&](Status status, const hidl_vec<uint8_t>& id) { - if (status == Status::OK) { - sessionId = toVector(id); + Return<void> hResult; + if (mPluginV1_1 == NULL || !setSecurityLevel) { + hResult = mPlugin->openSession( + [&](Status status,const hidl_vec<uint8_t>& id) { + if (status == Status::OK) { + sessionId = toVector(id); + } + err = toStatusT(status); } - err = toStatusT(status); - } - ); + ); + } else { + hResult = mPluginV1_1->openSession_1_1(hSecurityLevel, + [&](Status status, const hidl_vec<uint8_t>& id) { + if (status == Status::OK) { + sessionId = toVector(id); + } + err = toStatusT(status); + } + ); + } if (!hResult.isOk()) { err = DEAD_OBJECT; @@ -485,16 +626,16 @@ status_t DrmHal::openSession(Vector<uint8_t> &sessionId) { DrmSessionManager::Instance()->addSession(getCallingPid(), mDrmSessionClient, sessionId); mOpenSessions.push(sessionId); + mMetrics.SetSessionStart(sessionId); } + + mMetrics.mOpenSessionCounter.Increment(err); return err; } status_t DrmHal::closeSession(Vector<uint8_t> const &sessionId) { Mutex::Autolock autoLock(mLock); - - if (mInitCheck != OK) { - return mInitCheck; - } + INIT_CHECK(); Return<Status> status = mPlugin->closeSession(toHidlVec(sessionId)); if (status.isOk()) { @@ -507,9 +648,12 @@ status_t DrmHal::closeSession(Vector<uint8_t> const &sessionId) { } } } - reportMetrics(); - return toStatusT(status); + status_t response = toStatusT(status); + mMetrics.SetSessionEnd(sessionId); + mMetrics.mCloseSessionCounter.Increment(response); + return response; } + mMetrics.mCloseSessionCounter.Increment(DEAD_OBJECT); return DEAD_OBJECT; } @@ -519,10 +663,8 @@ status_t DrmHal::getKeyRequest(Vector<uint8_t> const &sessionId, String8> const &optionalParameters, Vector<uint8_t> &request, String8 &defaultUrl, DrmPlugin::KeyRequestType *keyRequestType) { Mutex::Autolock autoLock(mLock); - - if (mInitCheck != OK) { - return mInitCheck; - } + INIT_CHECK(); + EventTimer<status_t> keyRequestTimer(&mMetrics.mGetKeyRequestTimeUs); DrmSessionManager::Instance()->useSession(sessionId); @@ -534,6 +676,7 @@ status_t DrmHal::getKeyRequest(Vector<uint8_t> const &sessionId, } else if (keyType == DrmPlugin::kKeyType_Release) { hKeyType = KeyType::RELEASE; } else { + keyRequestTimer.SetAttribute(BAD_VALUE); return BAD_VALUE; } @@ -541,23 +684,63 @@ status_t DrmHal::getKeyRequest(Vector<uint8_t> const &sessionId, status_t err = UNKNOWN_ERROR; + if (mPluginV1_1 != NULL) { + Return<void> hResult = + mPluginV1_1->getKeyRequest_1_1( + toHidlVec(sessionId), toHidlVec(initData), + toHidlString(mimeType), hKeyType, hOptionalParameters, + [&](Status status, const hidl_vec<uint8_t>& hRequest, + drm::V1_1::KeyRequestType hKeyRequestType, + const hidl_string& hDefaultUrl) { + + if (status == Status::OK) { + request = toVector(hRequest); + defaultUrl = toString8(hDefaultUrl); + + switch (hKeyRequestType) { + case drm::V1_1::KeyRequestType::INITIAL: + *keyRequestType = DrmPlugin::kKeyRequestType_Initial; + break; + case drm::V1_1::KeyRequestType::RENEWAL: + *keyRequestType = DrmPlugin::kKeyRequestType_Renewal; + break; + case drm::V1_1::KeyRequestType::RELEASE: + *keyRequestType = DrmPlugin::kKeyRequestType_Release; + break; + case drm::V1_1::KeyRequestType::NONE: + *keyRequestType = DrmPlugin::kKeyRequestType_None; + break; + case drm::V1_1::KeyRequestType::UPDATE: + *keyRequestType = DrmPlugin::kKeyRequestType_Update; + break; + default: + *keyRequestType = DrmPlugin::kKeyRequestType_Unknown; + break; + } + err = toStatusT(status); + } + }); + return hResult.isOk() ? err : DEAD_OBJECT; + } + Return<void> hResult = mPlugin->getKeyRequest(toHidlVec(sessionId), toHidlVec(initData), toHidlString(mimeType), hKeyType, hOptionalParameters, [&](Status status, const hidl_vec<uint8_t>& hRequest, - KeyRequestType hKeyRequestType, const hidl_string& hDefaultUrl) { + drm::V1_0::KeyRequestType hKeyRequestType, + const hidl_string& hDefaultUrl) { if (status == Status::OK) { request = toVector(hRequest); defaultUrl = toString8(hDefaultUrl); switch (hKeyRequestType) { - case KeyRequestType::INITIAL: + case drm::V1_0::KeyRequestType::INITIAL: *keyRequestType = DrmPlugin::kKeyRequestType_Initial; break; - case KeyRequestType::RENEWAL: + case drm::V1_0::KeyRequestType::RENEWAL: *keyRequestType = DrmPlugin::kKeyRequestType_Renewal; break; - case KeyRequestType::RELEASE: + case drm::V1_0::KeyRequestType::RELEASE: *keyRequestType = DrmPlugin::kKeyRequestType_Release; break; default: @@ -568,16 +751,17 @@ status_t DrmHal::getKeyRequest(Vector<uint8_t> const &sessionId, } }); - return hResult.isOk() ? err : DEAD_OBJECT; + err = hResult.isOk() ? err : DEAD_OBJECT; + keyRequestTimer.SetAttribute(err); + return err; } status_t DrmHal::provideKeyResponse(Vector<uint8_t> const &sessionId, Vector<uint8_t> const &response, Vector<uint8_t> &keySetId) { Mutex::Autolock autoLock(mLock); + EventTimer<status_t> keyResponseTimer(&mMetrics.mProvideKeyResponseTimeUs); - if (mInitCheck != OK) { - return mInitCheck; - } + INIT_CHECK(); DrmSessionManager::Instance()->useSession(sessionId); @@ -592,41 +776,35 @@ status_t DrmHal::provideKeyResponse(Vector<uint8_t> const &sessionId, err = toStatusT(status); } ); - - return hResult.isOk() ? err : DEAD_OBJECT; + err = hResult.isOk() ? err : DEAD_OBJECT; + keyResponseTimer.SetAttribute(err); + return err; } status_t DrmHal::removeKeys(Vector<uint8_t> const &keySetId) { Mutex::Autolock autoLock(mLock); + INIT_CHECK(); - if (mInitCheck != OK) { - return mInitCheck; - } - - return toStatusT(mPlugin->removeKeys(toHidlVec(keySetId))); + Return<Status> status = mPlugin->removeKeys(toHidlVec(keySetId)); + return status.isOk() ? toStatusT(status) : DEAD_OBJECT; } status_t DrmHal::restoreKeys(Vector<uint8_t> const &sessionId, Vector<uint8_t> const &keySetId) { Mutex::Autolock autoLock(mLock); - - if (mInitCheck != OK) { - return mInitCheck; - } + INIT_CHECK(); DrmSessionManager::Instance()->useSession(sessionId); - return toStatusT(mPlugin->restoreKeys(toHidlVec(sessionId), - toHidlVec(keySetId))); + Return<Status> status = mPlugin->restoreKeys(toHidlVec(sessionId), + toHidlVec(keySetId)); + return status.isOk() ? toStatusT(status) : DEAD_OBJECT; } status_t DrmHal::queryKeyStatus(Vector<uint8_t> const &sessionId, KeyedVector<String8, String8> &infoMap) const { Mutex::Autolock autoLock(mLock); - - if (mInitCheck != OK) { - return mInitCheck; - } + INIT_CHECK(); DrmSessionManager::Instance()->useSession(sessionId); @@ -650,10 +828,7 @@ status_t DrmHal::getProvisionRequest(String8 const &certType, String8 const &certAuthority, Vector<uint8_t> &request, String8 &defaultUrl) { Mutex::Autolock autoLock(mLock); - - if (mInitCheck != OK) { - return mInitCheck; - } + INIT_CHECK(); status_t err = UNKNOWN_ERROR; @@ -669,16 +844,15 @@ status_t DrmHal::getProvisionRequest(String8 const &certType, } ); - return hResult.isOk() ? err : DEAD_OBJECT; + err = hResult.isOk() ? err : DEAD_OBJECT; + mMetrics.mGetProvisionRequestCounter.Increment(err); + return err; } status_t DrmHal::provideProvisionResponse(Vector<uint8_t> const &response, Vector<uint8_t> &certificate, Vector<uint8_t> &wrappedKey) { Mutex::Autolock autoLock(mLock); - - if (mInitCheck != OK) { - return mInitCheck; - } + INIT_CHECK(); status_t err = UNKNOWN_ERROR; @@ -693,15 +867,14 @@ status_t DrmHal::provideProvisionResponse(Vector<uint8_t> const &response, } ); - return hResult.isOk() ? err : DEAD_OBJECT; + err = hResult.isOk() ? err : DEAD_OBJECT; + mMetrics.mProvideProvisionResponseCounter.Increment(err); + return err; } status_t DrmHal::getSecureStops(List<Vector<uint8_t>> &secureStops) { Mutex::Autolock autoLock(mLock); - - if (mInitCheck != OK) { - return mInitCheck; - } + INIT_CHECK(); status_t err = UNKNOWN_ERROR; @@ -718,13 +891,36 @@ status_t DrmHal::getSecureStops(List<Vector<uint8_t>> &secureStops) { } -status_t DrmHal::getSecureStop(Vector<uint8_t> const &ssid, Vector<uint8_t> &secureStop) { +status_t DrmHal::getSecureStopIds(List<Vector<uint8_t>> &secureStopIds) { Mutex::Autolock autoLock(mLock); if (mInitCheck != OK) { return mInitCheck; } + if (mPluginV1_1 == NULL) { + return ERROR_DRM_CANNOT_HANDLE; + } + + status_t err = UNKNOWN_ERROR; + + Return<void> hResult = mPluginV1_1->getSecureStopIds( + [&](Status status, const hidl_vec<SecureStopId>& hSecureStopIds) { + if (status == Status::OK) { + secureStopIds = toSecureStopIds(hSecureStopIds); + } + err = toStatusT(status); + } + ); + + return hResult.isOk() ? err : DEAD_OBJECT; +} + + +status_t DrmHal::getSecureStop(Vector<uint8_t> const &ssid, Vector<uint8_t> &secureStop) { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + status_t err = UNKNOWN_ERROR; Return<void> hResult = mPlugin->getSecureStop(toHidlVec(ssid), @@ -741,22 +937,132 @@ status_t DrmHal::getSecureStop(Vector<uint8_t> const &ssid, Vector<uint8_t> &sec status_t DrmHal::releaseSecureStops(Vector<uint8_t> const &ssRelease) { Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + Return<Status> status(Status::ERROR_DRM_UNKNOWN); + if (mPluginV1_1 != NULL) { + SecureStopRelease secureStopRelease; + secureStopRelease.opaqueData = toHidlVec(ssRelease); + status = mPluginV1_1->releaseSecureStops(secureStopRelease); + } else { + status = mPlugin->releaseSecureStop(toHidlVec(ssRelease)); + } + return status.isOk() ? toStatusT(status) : DEAD_OBJECT; +} + +status_t DrmHal::removeSecureStop(Vector<uint8_t> const &ssid) { + Mutex::Autolock autoLock(mLock); if (mInitCheck != OK) { return mInitCheck; } - return toStatusT(mPlugin->releaseSecureStop(toHidlVec(ssRelease))); + if (mPluginV1_1 == NULL) { + return ERROR_DRM_CANNOT_HANDLE; + } + + Return<Status> status = mPluginV1_1->removeSecureStop(toHidlVec(ssid)); + return status.isOk() ? toStatusT(status) : DEAD_OBJECT; } -status_t DrmHal::releaseAllSecureStops() { +status_t DrmHal::removeAllSecureStops() { Mutex::Autolock autoLock(mLock); + INIT_CHECK(); - if (mInitCheck != OK) { - return mInitCheck; + Return<Status> status(Status::ERROR_DRM_UNKNOWN); + if (mPluginV1_1 != NULL) { + status = mPluginV1_1->removeAllSecureStops(); + } else { + status = mPlugin->releaseAllSecureStops(); + } + return status.isOk() ? toStatusT(status) : DEAD_OBJECT; +} + +status_t DrmHal::getHdcpLevels(DrmPlugin::HdcpLevel *connected, + DrmPlugin::HdcpLevel *max) const { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + if (connected == NULL || max == NULL) { + return BAD_VALUE; + } + status_t err = UNKNOWN_ERROR; + + if (mPluginV1_1 == NULL) { + return ERROR_DRM_CANNOT_HANDLE; } - return toStatusT(mPlugin->releaseAllSecureStops()); + *connected = DrmPlugin::kHdcpLevelUnknown; + *max = DrmPlugin::kHdcpLevelUnknown; + + Return<void> hResult = mPluginV1_1->getHdcpLevels( + [&](Status status, const HdcpLevel& hConnected, const HdcpLevel& hMax) { + if (status == Status::OK) { + *connected = toHdcpLevel(hConnected); + *max = toHdcpLevel(hMax); + } + err = toStatusT(status); + } + ); + + return hResult.isOk() ? err : DEAD_OBJECT; +} + +status_t DrmHal::getNumberOfSessions(uint32_t *open, uint32_t *max) const { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + if (open == NULL || max == NULL) { + return BAD_VALUE; + } + status_t err = UNKNOWN_ERROR; + + *open = 0; + *max = 0; + + if (mPluginV1_1 == NULL) { + return ERROR_DRM_CANNOT_HANDLE; + } + + Return<void> hResult = mPluginV1_1->getNumberOfSessions( + [&](Status status, uint32_t hOpen, uint32_t hMax) { + if (status == Status::OK) { + *open = hOpen; + *max = hMax; + } + err = toStatusT(status); + } + ); + + return hResult.isOk() ? err : DEAD_OBJECT; +} + +status_t DrmHal::getSecurityLevel(Vector<uint8_t> const &sessionId, + DrmPlugin::SecurityLevel *level) const { + Mutex::Autolock autoLock(mLock); + INIT_CHECK(); + + if (level == NULL) { + return BAD_VALUE; + } + status_t err = UNKNOWN_ERROR; + + if (mPluginV1_1 == NULL) { + return ERROR_DRM_CANNOT_HANDLE; + } + + *level = DrmPlugin::kSecurityLevelUnknown; + + Return<void> hResult = mPluginV1_1->getSecurityLevel(toHidlVec(sessionId), + [&](Status status, SecurityLevel hLevel) { + if (status == Status::OK) { + *level = toSecurityLevel(hLevel); + } + err = toStatusT(status); + } + ); + + return hResult.isOk() ? err : DEAD_OBJECT; } status_t DrmHal::getPropertyString(String8 const &name, String8 &value ) const { @@ -767,10 +1073,7 @@ status_t DrmHal::getPropertyString(String8 const &name, String8 &value ) const { status_t DrmHal::getPropertyStringInternal(String8 const &name, String8 &value) const { // This function is internal to the class and should only be called while // mLock is already held. - - if (mInitCheck != OK) { - return mInitCheck; - } + INIT_CHECK(); status_t err = UNKNOWN_ERROR; @@ -794,10 +1097,7 @@ status_t DrmHal::getPropertyByteArray(String8 const &name, Vector<uint8_t> &valu status_t DrmHal::getPropertyByteArrayInternal(String8 const &name, Vector<uint8_t> &value ) const { // This function is internal to the class and should only be called while // mLock is already held. - - if (mInitCheck != OK) { - return mInitCheck; - } + INIT_CHECK(); status_t err = UNKNOWN_ERROR; @@ -810,73 +1110,106 @@ status_t DrmHal::getPropertyByteArrayInternal(String8 const &name, Vector<uint8_ } ); - return hResult.isOk() ? err : DEAD_OBJECT; + err = hResult.isOk() ? err : DEAD_OBJECT; + if (name == kPropertyDeviceUniqueId) { + mMetrics.mGetDeviceUniqueIdCounter.Increment(err); + } + return err; } status_t DrmHal::setPropertyString(String8 const &name, String8 const &value ) const { Mutex::Autolock autoLock(mLock); + INIT_CHECK(); - if (mInitCheck != OK) { - return mInitCheck; - } - - Status status = mPlugin->setPropertyString(toHidlString(name), + Return<Status> status = mPlugin->setPropertyString(toHidlString(name), toHidlString(value)); - return toStatusT(status); + return status.isOk() ? toStatusT(status) : DEAD_OBJECT; } status_t DrmHal::setPropertyByteArray(String8 const &name, Vector<uint8_t> const &value ) const { Mutex::Autolock autoLock(mLock); + INIT_CHECK(); - if (mInitCheck != OK) { - return mInitCheck; - } - - Status status = mPlugin->setPropertyByteArray(toHidlString(name), + Return<Status> status = mPlugin->setPropertyByteArray(toHidlString(name), toHidlVec(value)); - return toStatusT(status); + return status.isOk() ? toStatusT(status) : DEAD_OBJECT; } +status_t DrmHal::getMetrics(PersistableBundle* metrics) { + if (metrics == nullptr) { + return UNEXPECTED_NULL; + } + mMetrics.Export(metrics); + + // Append vendor metrics if they are supported. + if (mPluginV1_1 != NULL) { + String8 vendor; + String8 description; + if (getPropertyStringInternal(String8("vendor"), vendor) != OK + || vendor.isEmpty()) { + ALOGE("Get vendor failed or is empty"); + vendor = "NONE"; + } + if (getPropertyStringInternal(String8("description"), description) != OK + || description.isEmpty()) { + ALOGE("Get description failed or is empty."); + description = "NONE"; + } + vendor += "."; + vendor += description; + + hidl_vec<DrmMetricGroup> pluginMetrics; + status_t err = UNKNOWN_ERROR; + + Return<void> status = mPluginV1_1->getMetrics( + [&](Status status, hidl_vec<DrmMetricGroup> pluginMetrics) { + if (status != Status::OK) { + ALOGV("Error getting plugin metrics: %d", status); + } else { + PersistableBundle pluginBundle; + if (MediaDrmMetrics::HidlMetricsToBundle( + pluginMetrics, &pluginBundle) == OK) { + metrics->putPersistableBundle(String16(vendor), pluginBundle); + } + } + err = toStatusT(status); + }); + return status.isOk() ? err : DEAD_OBJECT; + } + + return OK; +} status_t DrmHal::setCipherAlgorithm(Vector<uint8_t> const &sessionId, String8 const &algorithm) { Mutex::Autolock autoLock(mLock); - - if (mInitCheck != OK) { - return mInitCheck; - } + INIT_CHECK(); DrmSessionManager::Instance()->useSession(sessionId); - Status status = mPlugin->setCipherAlgorithm(toHidlVec(sessionId), + Return<Status> status = mPlugin->setCipherAlgorithm(toHidlVec(sessionId), toHidlString(algorithm)); - return toStatusT(status); + return status.isOk() ? toStatusT(status) : DEAD_OBJECT; } status_t DrmHal::setMacAlgorithm(Vector<uint8_t> const &sessionId, String8 const &algorithm) { Mutex::Autolock autoLock(mLock); - - if (mInitCheck != OK) { - return mInitCheck; - } + INIT_CHECK(); DrmSessionManager::Instance()->useSession(sessionId); - Status status = mPlugin->setMacAlgorithm(toHidlVec(sessionId), + Return<Status> status = mPlugin->setMacAlgorithm(toHidlVec(sessionId), toHidlString(algorithm)); - return toStatusT(status); + return status.isOk() ? toStatusT(status) : DEAD_OBJECT; } status_t DrmHal::encrypt(Vector<uint8_t> const &sessionId, Vector<uint8_t> const &keyId, Vector<uint8_t> const &input, Vector<uint8_t> const &iv, Vector<uint8_t> &output) { Mutex::Autolock autoLock(mLock); - - if (mInitCheck != OK) { - return mInitCheck; - } + INIT_CHECK(); DrmSessionManager::Instance()->useSession(sessionId); @@ -899,10 +1232,7 @@ status_t DrmHal::decrypt(Vector<uint8_t> const &sessionId, Vector<uint8_t> const &keyId, Vector<uint8_t> const &input, Vector<uint8_t> const &iv, Vector<uint8_t> &output) { Mutex::Autolock autoLock(mLock); - - if (mInitCheck != OK) { - return mInitCheck; - } + INIT_CHECK(); DrmSessionManager::Instance()->useSession(sessionId); @@ -925,10 +1255,7 @@ status_t DrmHal::sign(Vector<uint8_t> const &sessionId, Vector<uint8_t> const &keyId, Vector<uint8_t> const &message, Vector<uint8_t> &signature) { Mutex::Autolock autoLock(mLock); - - if (mInitCheck != OK) { - return mInitCheck; - } + INIT_CHECK(); DrmSessionManager::Instance()->useSession(sessionId); @@ -951,10 +1278,7 @@ status_t DrmHal::verify(Vector<uint8_t> const &sessionId, Vector<uint8_t> const &keyId, Vector<uint8_t> const &message, Vector<uint8_t> const &signature, bool &match) { Mutex::Autolock autoLock(mLock); - - if (mInitCheck != OK) { - return mInitCheck; - } + INIT_CHECK(); DrmSessionManager::Instance()->useSession(sessionId); @@ -979,10 +1303,7 @@ status_t DrmHal::signRSA(Vector<uint8_t> const &sessionId, String8 const &algorithm, Vector<uint8_t> const &message, Vector<uint8_t> const &wrappedKey, Vector<uint8_t> &signature) { Mutex::Autolock autoLock(mLock); - - if (mInitCheck != OK) { - return mInitCheck; - } + INIT_CHECK(); if (!checkPermission("android.permission.ACCESS_DRM_CERTIFICATES")) { return -EPERM; @@ -1007,17 +1328,7 @@ status_t DrmHal::signRSA(Vector<uint8_t> const &sessionId, void DrmHal::binderDied(const wp<IBinder> &the_late_who __unused) { - Mutex::Autolock autoLock(mLock); - closeOpenSessions(); - setListener(NULL); - mInitCheck = NO_INIT; - - if (mPlugin != NULL) { - if (!mPlugin->setListener(NULL).isOk()) { - mInitCheck = DEAD_OBJECT; - } - } - mPlugin.clear(); + cleanup(); } void DrmHal::writeByteArray(Parcel &obj, hidl_vec<uint8_t> const &vec) @@ -1030,18 +1341,55 @@ void DrmHal::writeByteArray(Parcel &obj, hidl_vec<uint8_t> const &vec) } } -void DrmHal::reportMetrics() const +void DrmHal::reportFrameworkMetrics() const +{ + MediaAnalyticsItem item("mediadrm"); + item.generateSessionID(); + item.setPkgName(mMetrics.GetAppPackageName().c_str()); + String8 vendor; + String8 description; + status_t result = getPropertyStringInternal(String8("vendor"), vendor); + if (result != OK) { + ALOGE("Failed to get vendor from drm plugin: %d", result); + } else { + item.setCString("vendor", vendor.c_str()); + } + result = getPropertyStringInternal(String8("description"), description); + if (result != OK) { + ALOGE("Failed to get description from drm plugin: %d", result); + } else { + item.setCString("description", description.c_str()); + } + + std::string serializedMetrics; + result = mMetrics.GetSerializedMetrics(&serializedMetrics); + if (result != OK) { + ALOGE("Failed to serialize framework metrics: %d", result); + } + std::string b64EncodedMetrics = toBase64StringNoPad(serializedMetrics.data(), + serializedMetrics.size()); + if (!b64EncodedMetrics.empty()) { + item.setCString("serialized_metrics", b64EncodedMetrics.c_str()); + } + if (!item.selfrecord()) { + ALOGE("Failed to self record framework metrics"); + } +} + +void DrmHal::reportPluginMetrics() const { - Vector<uint8_t> metrics; + Vector<uint8_t> metricsVector; String8 vendor; String8 description; if (getPropertyStringInternal(String8("vendor"), vendor) == OK && getPropertyStringInternal(String8("description"), description) == OK && - getPropertyByteArrayInternal(String8("metrics"), metrics) == OK) { - status_t res = android::reportDrmPluginMetrics( - metrics, vendor, description); + getPropertyByteArrayInternal(String8("metrics"), metricsVector) == OK) { + std::string metricsString = toBase64StringNoPad(metricsVector.array(), + metricsVector.size()); + status_t res = android::reportDrmPluginMetrics(metricsString, vendor, + description, mAppPackageName); if (res != OK) { - ALOGE("Metrics were retrieved but could not be reported: %i", res); + ALOGE("Metrics were retrieved but could not be reported: %d", res); } } } diff --git a/drm/libmediadrm/DrmMetrics.cpp b/drm/libmediadrm/DrmMetrics.cpp new file mode 100644 index 0000000000..4fed707017 --- /dev/null +++ b/drm/libmediadrm/DrmMetrics.cpp @@ -0,0 +1,422 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define LOG_TAG "DrmMetrics" +#include <iomanip> +#include <utility> + +#include <android-base/macros.h> +#include <media/stagefright/foundation/base64.h> +#include <mediadrm/DrmMetrics.h> +#include <sys/time.h> +#include <utils/Log.h> +#include <utils/Timers.h> + +#include "protos/metrics.pb.h" + +using ::android::String16; +using ::android::String8; +using ::android::drm_metrics::DrmFrameworkMetrics; +using ::android::hardware::hidl_string; +using ::android::hardware::hidl_vec; +using ::android::hardware::drm::V1_0::EventType; +using ::android::hardware::drm::V1_0::KeyStatusType; +using ::android::hardware::drm::V1_1::DrmMetricGroup; +using ::android::os::PersistableBundle; + +namespace { + +template <typename T> std::string GetAttributeName(T type); + +template <> std::string GetAttributeName<KeyStatusType>(KeyStatusType type) { + static const char *type_names[] = {"USABLE", "EXPIRED", + "OUTPUT_NOT_ALLOWED", "STATUS_PENDING", + "INTERNAL_ERROR"}; + if (((size_t)type) > arraysize(type_names)) { + return "UNKNOWN_TYPE"; + } + return type_names[(size_t)type]; +} + +template <> std::string GetAttributeName<EventType>(EventType type) { + static const char *type_names[] = {"PROVISION_REQUIRED", "KEY_NEEDED", + "KEY_EXPIRED", "VENDOR_DEFINED", + "SESSION_RECLAIMED"}; + if (((size_t)type) > arraysize(type_names)) { + return "UNKNOWN_TYPE"; + } + return type_names[(size_t)type]; +} + +template <typename T> +void ExportCounterMetric(const android::CounterMetric<T> &counter, + PersistableBundle *metrics) { + if (!metrics) { + ALOGE("metrics was unexpectedly null."); + return; + } + std::string success_count_name = counter.metric_name() + ".ok.count"; + std::string error_count_name = counter.metric_name() + ".error.count"; + std::vector<int64_t> status_values; + counter.ExportValues( + [&](const android::status_t status, const int64_t value) { + if (status == android::OK) { + metrics->putLong(android::String16(success_count_name.c_str()), + value); + } else { + int64_t total_errors(0); + metrics->getLong(android::String16(error_count_name.c_str()), + &total_errors); + metrics->putLong(android::String16(error_count_name.c_str()), + total_errors + value); + status_values.push_back(status); + } + }); + if (!status_values.empty()) { + std::string error_list_name = counter.metric_name() + ".error.list"; + metrics->putLongVector(android::String16(error_list_name.c_str()), + status_values); + } +} + +template <typename T> +void ExportCounterMetricWithAttributeNames( + const android::CounterMetric<T> &counter, PersistableBundle *metrics) { + if (!metrics) { + ALOGE("metrics was unexpectedly null."); + return; + } + counter.ExportValues([&](const T &attribute, const int64_t value) { + std::string name = counter.metric_name() + "." + + GetAttributeName(attribute) + ".count"; + metrics->putLong(android::String16(name.c_str()), value); + }); +} + +template <typename T> +void ExportEventMetric(const android::EventMetric<T> &event, + PersistableBundle *metrics) { + if (!metrics) { + ALOGE("metrics was unexpectedly null."); + return; + } + std::string success_count_name = event.metric_name() + ".ok.count"; + std::string error_count_name = event.metric_name() + ".error.count"; + std::string timing_name = event.metric_name() + ".ok.average_time_micros"; + std::vector<int64_t> status_values; + event.ExportValues([&](const android::status_t &status, + const android::EventStatistics &value) { + if (status == android::OK) { + metrics->putLong(android::String16(success_count_name.c_str()), + value.count); + metrics->putLong(android::String16(timing_name.c_str()), + value.mean); + } else { + int64_t total_errors(0); + metrics->getLong(android::String16(error_count_name.c_str()), + &total_errors); + metrics->putLong(android::String16(error_count_name.c_str()), + total_errors + value.count); + status_values.push_back(status); + } + }); + if (!status_values.empty()) { + std::string error_list_name = event.metric_name() + ".error.list"; + metrics->putLongVector(android::String16(error_list_name.c_str()), + status_values); + } +} + +void ExportSessionLifespans( + const std::map<std::string, std::pair<int64_t, int64_t>> &mSessionLifespans, + PersistableBundle *metrics) { + if (!metrics) { + ALOGE("metrics was unexpectedly null."); + return; + } + + if (mSessionLifespans.empty()) { + return; + } + + PersistableBundle startTimesBundle; + PersistableBundle endTimesBundle; + for (auto it = mSessionLifespans.begin(); it != mSessionLifespans.end(); + it++) { + String16 key(it->first.c_str(), it->first.size()); + startTimesBundle.putLong(key, it->second.first); + endTimesBundle.putLong(key, it->second.second); + } + metrics->putPersistableBundle( + android::String16("drm.mediadrm.session_start_times_ms"), + startTimesBundle); + metrics->putPersistableBundle( + android::String16("drm.mediadrm.session_end_times_ms"), endTimesBundle); +} + +std::string ToHexString(const android::Vector<uint8_t> &sessionId) { + std::ostringstream out; + out << std::hex << std::setfill('0'); + for (size_t i = 0; i < sessionId.size(); i++) { + out << std::setw(2) << (int)(sessionId[i]); + } + return out.str(); +} + +template <typename CT> +void SetValue(const String16 &name, DrmMetricGroup::ValueType type, + const CT &value, PersistableBundle *bundle) { + switch (type) { + case DrmMetricGroup::ValueType::INT64_TYPE: + bundle->putLong(name, value.int64Value); + break; + case DrmMetricGroup::ValueType::DOUBLE_TYPE: + bundle->putDouble(name, value.doubleValue); + break; + case DrmMetricGroup::ValueType::STRING_TYPE: + bundle->putString(name, String16(value.stringValue.c_str())); + break; + default: + ALOGE("Unexpected value type: %hhu", type); + } +} + +inline String16 MakeIndexString(unsigned int index) { + std::string str("["); + str.append(std::to_string(index)); + str.append("]"); + return String16(str.c_str()); +} + +} // namespace + +namespace android { + +MediaDrmMetrics::MediaDrmMetrics() + : mOpenSessionCounter("drm.mediadrm.open_session", "status"), + mCloseSessionCounter("drm.mediadrm.close_session", "status"), + mGetKeyRequestTimeUs("drm.mediadrm.get_key_request", "status"), + mProvideKeyResponseTimeUs("drm.mediadrm.provide_key_response", "status"), + mGetProvisionRequestCounter("drm.mediadrm.get_provision_request", + "status"), + mProvideProvisionResponseCounter( + "drm.mediadrm.provide_provision_response", "status"), + mKeyStatusChangeCounter("drm.mediadrm.key_status_change", + "key_status_type"), + mEventCounter("drm.mediadrm.event", "event_type"), + mGetDeviceUniqueIdCounter("drm.mediadrm.get_device_unique_id", "status") { +} + +void MediaDrmMetrics::SetSessionStart( + const android::Vector<uint8_t> &sessionId) { + std::string sessionIdHex = ToHexString(sessionId); + mSessionLifespans[sessionIdHex] = + std::make_pair(GetCurrentTimeMs(), (int64_t)0); +} + +void MediaDrmMetrics::SetSessionEnd(const android::Vector<uint8_t> &sessionId) { + std::string sessionIdHex = ToHexString(sessionId); + int64_t endTimeMs = GetCurrentTimeMs(); + if (mSessionLifespans.find(sessionIdHex) != mSessionLifespans.end()) { + mSessionLifespans[sessionIdHex] = + std::make_pair(mSessionLifespans[sessionIdHex].first, endTimeMs); + } else { + mSessionLifespans[sessionIdHex] = std::make_pair((int64_t)0, endTimeMs); + } +} + +void MediaDrmMetrics::Export(PersistableBundle *metrics) { + if (!metrics) { + ALOGE("metrics was unexpectedly null."); + return; + } + ExportCounterMetric(mOpenSessionCounter, metrics); + ExportCounterMetric(mCloseSessionCounter, metrics); + ExportEventMetric(mGetKeyRequestTimeUs, metrics); + ExportEventMetric(mProvideKeyResponseTimeUs, metrics); + ExportCounterMetric(mGetProvisionRequestCounter, metrics); + ExportCounterMetric(mProvideProvisionResponseCounter, metrics); + ExportCounterMetricWithAttributeNames(mKeyStatusChangeCounter, metrics); + ExportCounterMetricWithAttributeNames(mEventCounter, metrics); + ExportCounterMetric(mGetDeviceUniqueIdCounter, metrics); + ExportSessionLifespans(mSessionLifespans, metrics); +} + +status_t MediaDrmMetrics::GetSerializedMetrics(std::string *serializedMetrics) { + + if (!serializedMetrics) { + ALOGE("serializedMetrics was unexpectedly null."); + return UNEXPECTED_NULL; + } + + DrmFrameworkMetrics metrics; + + mOpenSessionCounter.ExportValues( + [&](const android::status_t status, const int64_t value) { + DrmFrameworkMetrics::Counter *counter = + metrics.add_open_session_counter(); + counter->set_count(value); + counter->mutable_attributes()->set_error_code(status); + }); + + mCloseSessionCounter.ExportValues( + [&](const android::status_t status, const int64_t value) { + DrmFrameworkMetrics::Counter *counter = + metrics.add_close_session_counter(); + counter->set_count(value); + counter->mutable_attributes()->set_error_code(status); + }); + + mGetProvisionRequestCounter.ExportValues( + [&](const android::status_t status, const int64_t value) { + DrmFrameworkMetrics::Counter *counter = + metrics.add_get_provisioning_request_counter(); + counter->set_count(value); + counter->mutable_attributes()->set_error_code(status); + }); + + mProvideProvisionResponseCounter.ExportValues( + [&](const android::status_t status, const int64_t value) { + DrmFrameworkMetrics::Counter *counter = + metrics.add_provide_provisioning_response_counter(); + counter->set_count(value); + counter->mutable_attributes()->set_error_code(status); + }); + + mKeyStatusChangeCounter.ExportValues( + [&](const KeyStatusType key_status_type, const int64_t value) { + DrmFrameworkMetrics::Counter *counter = + metrics.add_key_status_change_counter(); + counter->set_count(value); + counter->mutable_attributes()->set_key_status_type( + (uint32_t)key_status_type); + }); + + mEventCounter.ExportValues( + [&](const EventType event_type, const int64_t value) { + DrmFrameworkMetrics::Counter *counter = + metrics.add_event_callback_counter(); + counter->set_count(value); + counter->mutable_attributes()->set_event_type((uint32_t)event_type); + }); + + mGetDeviceUniqueIdCounter.ExportValues( + [&](const status_t status, const int64_t value) { + DrmFrameworkMetrics::Counter *counter = + metrics.add_get_device_unique_id_counter(); + counter->set_count(value); + counter->mutable_attributes()->set_error_code(status); + }); + + mGetKeyRequestTimeUs.ExportValues( + [&](const status_t status, const EventStatistics &stats) { + DrmFrameworkMetrics::DistributionMetric *metric = + metrics.add_get_key_request_time_us(); + metric->set_min(stats.min); + metric->set_max(stats.max); + metric->set_mean(stats.mean); + metric->set_operation_count(stats.count); + metric->set_variance(stats.sum_squared_deviation / stats.count); + metric->mutable_attributes()->set_error_code(status); + }); + + mProvideKeyResponseTimeUs.ExportValues( + [&](const status_t status, const EventStatistics &stats) { + DrmFrameworkMetrics::DistributionMetric *metric = + metrics.add_provide_key_response_time_us(); + metric->set_min(stats.min); + metric->set_max(stats.max); + metric->set_mean(stats.mean); + metric->set_operation_count(stats.count); + metric->set_variance(stats.sum_squared_deviation / stats.count); + metric->mutable_attributes()->set_error_code(status); + }); + + for (const auto &sessionLifespan : mSessionLifespans) { + auto *map = metrics.mutable_session_lifetimes(); + + (*map)[sessionLifespan.first].set_start_time_ms( + sessionLifespan.second.first); + (*map)[sessionLifespan.first].set_end_time_ms( + sessionLifespan.second.second); + } + + if (!metrics.SerializeToString(serializedMetrics)) { + ALOGE("Failed to serialize metrics."); + return UNKNOWN_ERROR; + } + + return OK; +} + +int64_t MediaDrmMetrics::GetCurrentTimeMs() { + struct timeval tv; + gettimeofday(&tv, NULL); + return ((int64_t)tv.tv_sec * 1000) + ((int64_t)tv.tv_usec / 1000); +} + +status_t MediaDrmMetrics::HidlMetricsToBundle( + const hidl_vec<DrmMetricGroup> &hidlMetricGroups, + PersistableBundle *bundleMetricGroups) { + if (bundleMetricGroups == nullptr) { + return UNEXPECTED_NULL; + } + if (hidlMetricGroups.size() == 0) { + return OK; + } + + int groupIndex = 0; + std::map<String16, int> indexMap; + for (const auto &hidlMetricGroup : hidlMetricGroups) { + PersistableBundle bundleMetricGroup; + for (const auto &hidlMetric : hidlMetricGroup.metrics) { + String16 metricName(hidlMetric.name.c_str()); + PersistableBundle bundleMetric; + // Add metric component values. + for (const auto &value : hidlMetric.values) { + SetValue(String16(value.componentName.c_str()), value.type, + value, &bundleMetric); + } + // Set metric attributes. + PersistableBundle bundleMetricAttributes; + for (const auto &attribute : hidlMetric.attributes) { + SetValue(String16(attribute.name.c_str()), attribute.type, + attribute, &bundleMetricAttributes); + } + // Add attributes to the bundle metric. + bundleMetric.putPersistableBundle(String16("attributes"), + bundleMetricAttributes); + // Add one layer of indirection, allowing for repeated metric names. + PersistableBundle repeatedMetrics; + bundleMetricGroup.getPersistableBundle(metricName, + &repeatedMetrics); + int index = indexMap[metricName]; + repeatedMetrics.putPersistableBundle(MakeIndexString(index), + bundleMetric); + indexMap[metricName] = ++index; + + // Add the bundle metric to the group of metrics. + bundleMetricGroup.putPersistableBundle(metricName, + repeatedMetrics); + } + // Add the bundle metric group to the collection of groups. + bundleMetricGroups->putPersistableBundle(MakeIndexString(groupIndex++), + bundleMetricGroup); + } + + return OK; +} + +} // namespace android diff --git a/drm/libmediadrm/DrmPluginPath.cpp b/drm/libmediadrm/DrmPluginPath.cpp index c760825788..ac8607c278 100644 --- a/drm/libmediadrm/DrmPluginPath.cpp +++ b/drm/libmediadrm/DrmPluginPath.cpp @@ -19,7 +19,7 @@ #include <utils/Log.h> #include <cutils/properties.h> -#include <media/DrmPluginPath.h> +#include <mediadrm/DrmPluginPath.h> namespace android { diff --git a/drm/libmediadrm/DrmSessionManager.cpp b/drm/libmediadrm/DrmSessionManager.cpp index 02270d087c..375644ce87 100644 --- a/drm/libmediadrm/DrmSessionManager.cpp +++ b/drm/libmediadrm/DrmSessionManager.cpp @@ -21,9 +21,9 @@ #include <binder/IPCThreadState.h> #include <binder/IProcessInfoService.h> #include <binder/IServiceManager.h> -#include <media/DrmSessionManager.h> -#include <media/DrmSessionClientInterface.h> #include <media/stagefright/ProcessInfo.h> +#include <mediadrm/DrmSessionClientInterface.h> +#include <mediadrm/DrmSessionManager.h> #include <unistd.h> #include <utils/String8.h> diff --git a/drm/libmediadrm/ICrypto.cpp b/drm/libmediadrm/ICrypto.cpp index ee5ea936c4..a2594aa2d8 100644 --- a/drm/libmediadrm/ICrypto.cpp +++ b/drm/libmediadrm/ICrypto.cpp @@ -19,10 +19,10 @@ #include <binder/Parcel.h> #include <binder/IMemory.h> #include <cutils/log.h> -#include <media/ICrypto.h> #include <media/stagefright/MediaErrors.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AString.h> +#include <mediadrm/ICrypto.h> #include <utils/Log.h> namespace android { @@ -346,10 +346,10 @@ status_t BnCrypto::onTransact( return OK; } - CryptoPlugin::SubSample *subSamples = - new CryptoPlugin::SubSample[numSubSamples]; + std::unique_ptr<CryptoPlugin::SubSample[]> subSamples = + std::make_unique<CryptoPlugin::SubSample[]>(numSubSamples); - data.read(subSamples, + data.read(subSamples.get(), sizeof(CryptoPlugin::SubSample) * numSubSamples); DestinationBuffer destination; @@ -407,7 +407,7 @@ status_t BnCrypto::onTransact( result = -EINVAL; } else { result = decrypt(key, iv, mode, pattern, source, offset, - subSamples, numSubSamples, destination, &errorDetailMsg); + subSamples.get(), numSubSamples, destination, &errorDetailMsg); } reply->writeInt32(result); @@ -426,9 +426,7 @@ status_t BnCrypto::onTransact( } } - delete[] subSamples; - subSamples = NULL; - + subSamples.reset(); return OK; } diff --git a/drm/libmediadrm/IDrm.cpp b/drm/libmediadrm/IDrm.cpp index 8ff6e6a0c4..509961fb10 100644 --- a/drm/libmediadrm/IDrm.cpp +++ b/drm/libmediadrm/IDrm.cpp @@ -19,10 +19,10 @@ #include <utils/Log.h> #include <binder/Parcel.h> -#include <media/IDrm.h> #include <media/stagefright/MediaErrors.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AString.h> +#include <mediadrm/IDrm.h> namespace android { @@ -46,6 +46,7 @@ enum { GET_PROPERTY_BYTE_ARRAY, SET_PROPERTY_STRING, SET_PROPERTY_BYTE_ARRAY, + GET_METRICS, SET_CIPHER_ALGORITHM, SET_MAC_ALGORITHM, ENCRYPT, @@ -55,7 +56,12 @@ enum { VERIFY, SET_LISTENER, GET_SECURE_STOP, - RELEASE_ALL_SECURE_STOPS + REMOVE_ALL_SECURE_STOPS, + GET_HDCP_LEVELS, + GET_NUMBER_OF_SESSIONS, + GET_SECURITY_LEVEL, + REMOVE_SECURE_STOP, + GET_SECURE_STOP_IDS }; struct BpDrm : public BpInterface<IDrm> { @@ -114,9 +120,11 @@ struct BpDrm : public BpInterface<IDrm> { return reply.readInt32(); } - virtual status_t openSession(Vector<uint8_t> &sessionId) { + virtual status_t openSession(DrmPlugin::SecurityLevel securityLevel, + Vector<uint8_t> &sessionId) { Parcel data, reply; data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + data.writeInt32(securityLevel); status_t status = remote()->transact(OPEN_SESSION, data, &reply); if (status != OK) { @@ -297,6 +305,25 @@ struct BpDrm : public BpInterface<IDrm> { return reply.readInt32(); } + virtual status_t getSecureStopIds(List<Vector<uint8_t> > &secureStopIds) { + Parcel data, reply; + data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + + status_t status = remote()->transact(GET_SECURE_STOP_IDS, data, &reply); + if (status != OK) { + return status; + } + + secureStopIds.clear(); + uint32_t count = reply.readInt32(); + for (size_t i = 0; i < count; i++) { + Vector<uint8_t> secureStopId; + readVector(reply, secureStopId); + secureStopIds.push_back(secureStopId); + } + return reply.readInt32(); + } + virtual status_t getSecureStop(Vector<uint8_t> const &ssid, Vector<uint8_t> &secureStop) { Parcel data, reply; data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); @@ -324,11 +351,24 @@ struct BpDrm : public BpInterface<IDrm> { return reply.readInt32(); } - virtual status_t releaseAllSecureStops() { + virtual status_t removeSecureStop(Vector<uint8_t> const &ssid) { Parcel data, reply; data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); - status_t status = remote()->transact(RELEASE_ALL_SECURE_STOPS, data, &reply); + writeVector(data, ssid); + status_t status = remote()->transact(REMOVE_SECURE_STOP, data, &reply); + if (status != OK) { + return status; + } + + return reply.readInt32(); + } + + virtual status_t removeAllSecureStops() { + Parcel data, reply; + data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + + status_t status = remote()->transact(REMOVE_ALL_SECURE_STOPS, data, &reply); if (status != OK) { return status; } @@ -350,6 +390,65 @@ struct BpDrm : public BpInterface<IDrm> { return reply.readInt32(); } + virtual status_t getHdcpLevels(DrmPlugin::HdcpLevel *connected, + DrmPlugin::HdcpLevel *max) const { + Parcel data, reply; + + if (connected == NULL || max == NULL) { + return BAD_VALUE; + } + + data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + + status_t status = remote()->transact(GET_HDCP_LEVELS, data, &reply); + if (status != OK) { + return status; + } + + *connected = static_cast<DrmPlugin::HdcpLevel>(reply.readInt32()); + *max = static_cast<DrmPlugin::HdcpLevel>(reply.readInt32()); + return reply.readInt32(); + } + + virtual status_t getNumberOfSessions(uint32_t *open, uint32_t *max) const { + Parcel data, reply; + + if (open == NULL || max == NULL) { + return BAD_VALUE; + } + + data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + + status_t status = remote()->transact(GET_NUMBER_OF_SESSIONS, data, &reply); + if (status != OK) { + return status; + } + + *open = reply.readInt32(); + *max = reply.readInt32(); + return reply.readInt32(); + } + + virtual status_t getSecurityLevel(Vector<uint8_t> const &sessionId, + DrmPlugin::SecurityLevel *level) const { + Parcel data, reply; + + if (level == NULL) { + return BAD_VALUE; + } + + data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + + writeVector(data, sessionId); + status_t status = remote()->transact(GET_SECURITY_LEVEL, data, &reply); + if (status != OK) { + return status; + } + + *level = static_cast<DrmPlugin::SecurityLevel>(reply.readInt32()); + return reply.readInt32(); + } + virtual status_t getPropertyByteArray(String8 const &name, Vector<uint8_t> &value) const { Parcel data, reply; data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); @@ -393,6 +492,35 @@ struct BpDrm : public BpInterface<IDrm> { return reply.readInt32(); } + virtual status_t getMetrics(os::PersistableBundle *metrics) { + if (metrics == NULL) { + return BAD_VALUE; + } + Parcel data, reply; + data.writeInterfaceToken(IDrm::getInterfaceDescriptor()); + + status_t status = remote()->transact(GET_METRICS, data, &reply); + if (status != OK) { + return status; + } + // The reply data is ordered as + // 1) 32 bit integer reply followed by + // 2) Serialized PersistableBundle containing metrics. + status_t reply_status; + if (reply.readInt32(&reply_status) != OK + || reply_status != OK) { + ALOGE("Failed to read getMetrics response code from parcel. %d", + reply_status); + return reply_status; + } + + status = metrics->readFromParcel(&reply); + if (status != OK) { + ALOGE("Failed to read metrics from parcel. %d", status); + return status; + } + return reply_status; + } virtual status_t setCipherAlgorithm(Vector<uint8_t> const &sessionId, String8 const &algorithm) { @@ -615,8 +743,10 @@ status_t BnDrm::onTransact( case OPEN_SESSION: { CHECK_INTERFACE(IDrm, data, reply); + DrmPlugin::SecurityLevel level = + static_cast<DrmPlugin::SecurityLevel>(data.readInt32()); Vector<uint8_t> sessionId; - status_t result = openSession(sessionId); + status_t result = openSession(level, sessionId); writeVector(reply, sessionId); reply->writeInt32(result); return OK; @@ -761,6 +891,24 @@ status_t BnDrm::onTransact( return OK; } + case GET_SECURE_STOP_IDS: + { + CHECK_INTERFACE(IDrm, data, reply); + List<Vector<uint8_t> > secureStopIds; + status_t result = getSecureStopIds(secureStopIds); + size_t count = secureStopIds.size(); + reply->writeInt32(count); + List<Vector<uint8_t> >::iterator iter = secureStopIds.begin(); + while(iter != secureStopIds.end()) { + size_t size = iter->size(); + reply->writeInt32(size); + reply->write(iter->array(), iter->size()); + iter++; + } + reply->writeInt32(result); + return OK; + } + case GET_SECURE_STOP: { CHECK_INTERFACE(IDrm, data, reply); @@ -781,10 +929,54 @@ status_t BnDrm::onTransact( return OK; } - case RELEASE_ALL_SECURE_STOPS: + case REMOVE_SECURE_STOP: + { + CHECK_INTERFACE(IDrm, data, reply); + Vector<uint8_t> ssid; + readVector(data, ssid); + reply->writeInt32(removeSecureStop(ssid)); + return OK; + } + + case REMOVE_ALL_SECURE_STOPS: + { + CHECK_INTERFACE(IDrm, data, reply); + reply->writeInt32(removeAllSecureStops()); + return OK; + } + + case GET_HDCP_LEVELS: + { + CHECK_INTERFACE(IDrm, data, reply); + DrmPlugin::HdcpLevel connected = DrmPlugin::kHdcpLevelUnknown; + DrmPlugin::HdcpLevel max = DrmPlugin::kHdcpLevelUnknown; + status_t result = getHdcpLevels(&connected, &max); + reply->writeInt32(connected); + reply->writeInt32(max); + reply->writeInt32(result); + return OK; + } + + case GET_NUMBER_OF_SESSIONS: { CHECK_INTERFACE(IDrm, data, reply); - reply->writeInt32(releaseAllSecureStops()); + uint32_t open = 0, max = 0; + status_t result = getNumberOfSessions(&open, &max); + reply->writeInt32(open); + reply->writeInt32(max); + reply->writeInt32(result); + return OK; + } + + case GET_SECURITY_LEVEL: + { + CHECK_INTERFACE(IDrm, data, reply); + Vector<uint8_t> sessionId; + readVector(data, sessionId); + DrmPlugin::SecurityLevel level = DrmPlugin::kSecurityLevelUnknown; + status_t result = getSecurityLevel(sessionId, &level); + reply->writeInt32(level); + reply->writeInt32(result); return OK; } @@ -829,6 +1021,24 @@ status_t BnDrm::onTransact( return OK; } + case GET_METRICS: + { + CHECK_INTERFACE(IDrm, data, reply); + + os::PersistableBundle metrics; + status_t result = getMetrics(&metrics); + // The reply data is ordered as + // 1) 32 bit integer reply followed by + // 2) Serialized PersistableBundle containing metrics. + // Only write the metrics if the getMetrics result was + // OK and we successfully added the status to reply. + status_t parcel_result = reply->writeInt32(result); + if (result == OK && parcel_result == OK) { + parcel_result = metrics.writeToParcel(reply); + } + return parcel_result; + } + case SET_CIPHER_ALGORITHM: { CHECK_INTERFACE(IDrm, data, reply); diff --git a/drm/libmediadrm/IDrmClient.cpp b/drm/libmediadrm/IDrmClient.cpp index 444201fff4..357de9d6b1 100644 --- a/drm/libmediadrm/IDrmClient.cpp +++ b/drm/libmediadrm/IDrmClient.cpp @@ -24,7 +24,7 @@ #include <binder/Parcel.h> #include <media/IMediaPlayerClient.h> -#include <media/IDrmClient.h> +#include <mediadrm/IDrmClient.h> namespace android { diff --git a/drm/libmediadrm/IMediaDrmService.cpp b/drm/libmediadrm/IMediaDrmService.cpp index 84812dc01d..f320d0bb76 100644 --- a/drm/libmediadrm/IMediaDrmService.cpp +++ b/drm/libmediadrm/IMediaDrmService.cpp @@ -20,9 +20,9 @@ #include <binder/Parcel.h> #include <binder/IMemory.h> -#include <media/ICrypto.h> -#include <media/IDrm.h> -#include <media/IMediaDrmService.h> +#include <mediadrm/ICrypto.h> +#include <mediadrm/IDrm.h> +#include <mediadrm/IMediaDrmService.h> #include <utils/Errors.h> // for status_t #include <utils/String8.h> diff --git a/drm/libmediadrm/PluginMetricsReporting.cpp b/drm/libmediadrm/PluginMetricsReporting.cpp index 57ff5b8207..5cb48bf678 100644 --- a/drm/libmediadrm/PluginMetricsReporting.cpp +++ b/drm/libmediadrm/PluginMetricsReporting.cpp @@ -16,83 +16,35 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "PluginMetricsReporting" -#include <utils/Log.h> #include <media/PluginMetricsReporting.h> +#include <inttypes.h> + #include <media/MediaAnalyticsItem.h> +#include <utils/Log.h> -#include "protos/plugin_metrics.pb.h" namespace android { namespace { -using android::drm_metrics::MetricsGroup; -using android::drm_metrics::MetricsGroup_Metric; -using android::drm_metrics::MetricsGroup_Metric_MetricValue; - -const char* const kParentAttribute = "/parent/external"; +constexpr char kSerializedMetricsField[] = "serialized_metrics"; -status_t reportMetricsGroup(const MetricsGroup& metricsGroup, - const String8& batchName, - const int64_t* parentId) { - MediaAnalyticsItem analyticsItem(batchName.c_str()); +status_t reportVendorMetrics(const std::string& metrics, + const String8& name, + const String8& appPackageName) { + MediaAnalyticsItem analyticsItem(name.c_str()); analyticsItem.generateSessionID(); - int64_t sessionId = analyticsItem.getSessionID(); - if (parentId != NULL) { - analyticsItem.setInt64(kParentAttribute, *parentId); - } - - // Report the package name. - if (metricsGroup.has_app_package_name()) { - AString app_package_name(metricsGroup.app_package_name().c_str(), - metricsGroup.app_package_name().size()); - analyticsItem.setPkgName(app_package_name); - } - - for (int i = 0; i < metricsGroup.metric_size(); ++i) { - const MetricsGroup_Metric& metric = metricsGroup.metric(i); - if (!metric.has_name()) { - ALOGE("Metric with no name."); - return BAD_VALUE; - } - - if (!metric.has_value()) { - ALOGE("Metric with no value."); - return BAD_VALUE; - } - const MetricsGroup_Metric_MetricValue& value = metric.value(); - if (value.has_int_value()) { - analyticsItem.setInt64(metric.name().c_str(), - value.int_value()); - } else if (value.has_double_value()) { - analyticsItem.setDouble(metric.name().c_str(), - value.double_value()); - } else if (value.has_string_value()) { - analyticsItem.setCString(metric.name().c_str(), - value.string_value().c_str()); - } else { - ALOGE("Metric Value with no actual value."); - return BAD_VALUE; - } + std::string app_package_name(appPackageName.c_str(), appPackageName.size()); + analyticsItem.setPkgName(app_package_name); + if (metrics.size() > 0) { + analyticsItem.setCString(kSerializedMetricsField, metrics.c_str()); } - analyticsItem.setFinalized(true); if (!analyticsItem.selfrecord()) { - // Note the cast to int is because we build on 32 and 64 bit. - // The cast prevents a peculiar printf problem where one format cannot - // satisfy both. - ALOGE("selfrecord() returned false. sessioId %d", (int) sessionId); - } - - for (int i = 0; i < metricsGroup.metric_sub_group_size(); ++i) { - const MetricsGroup& subGroup = metricsGroup.metric_sub_group(i); - status_t res = reportMetricsGroup(subGroup, batchName, &sessionId); - if (res != OK) { - return res; - } + ALOGE("selfrecord() returned false. sessioId %" PRId64, analyticsItem.getSessionID()); } return OK; @@ -114,21 +66,16 @@ String8 sanitize(const String8& input) { } // namespace -status_t reportDrmPluginMetrics(const Vector<uint8_t>& serializedMetrics, +status_t reportDrmPluginMetrics(const std::string& b64EncodedMetrics, const String8& vendor, - const String8& description) { - MetricsGroup root_metrics_group; - if (!root_metrics_group.ParseFromArray(serializedMetrics.array(), - serializedMetrics.size())) { - ALOGE("Failure to parse."); - return BAD_VALUE; - } + const String8& description, + const String8& appPackageName) { String8 name = String8::format("drm.vendor.%s.%s", sanitize(vendor).c_str(), sanitize(description).c_str()); - return reportMetricsGroup(root_metrics_group, name, NULL); + return reportVendorMetrics(b64EncodedMetrics, name, appPackageName); } } // namespace android diff --git a/drm/libmediadrm/SharedLibrary.cpp b/drm/libmediadrm/SharedLibrary.cpp index bebafa8bb3..b2d635def8 100644 --- a/drm/libmediadrm/SharedLibrary.cpp +++ b/drm/libmediadrm/SharedLibrary.cpp @@ -19,7 +19,7 @@ #include <dlfcn.h> #include <media/stagefright/foundation/ADebug.h> -#include <media/SharedLibrary.h> +#include <mediadrm/SharedLibrary.h> #include <utils/Log.h> namespace android { diff --git a/drm/libmediadrm/protos/metrics.proto b/drm/libmediadrm/protos/metrics.proto new file mode 100644 index 0000000000..6160e6f272 --- /dev/null +++ b/drm/libmediadrm/protos/metrics.proto @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; + +package android.drm_metrics; + + +// This message contains the specific metrics captured by DrmMetrics. It is +// used for serializing and logging metrics. +// next id: 11. +message DrmFrameworkMetrics { + // TODO: Consider using extensions. + + // Attributes are associated with a recorded value. E.g. A counter may + // represent a count of an operation returning a specific error code. The + // error code will be an attribute. + message Attributes { + // Reserved for compatibility with logging proto. + reserved 2 to 13; + + // A general purpose error code where 0 means OK. + optional int32 error_code = 1; + + // Defined at ::android::hardware::drm::V1_0::KeyStatusType; + optional uint32 key_status_type = 14; + + // Defined at ::android::hardware::drm::V1_0::EventType; + optional uint32 event_type = 15; + } + + // The Counter message is used to store a count value with an associated + // Attribute. + message Counter { + optional uint64 count = 1; + // Represents the attributes associated with this counter instance. + optional Attributes attributes = 2; + } + + // The DistributionMetric is meant to capture the moments of a normally + // distributed (or approximately normal) value. + message DistributionMetric { + optional float min = 1; + optional float max = 2; + optional float mean = 3; + optional double variance = 4; + optional uint64 operation_count = 5; + + // Represents the attributes assocated with this distribution metric + // instance. + optional Attributes attributes = 6; + } + + message SessionLifetime { + // Start time of the session in milliseconds since epoch. + optional uint64 start_time_ms = 1; + // End time of the session in milliseconds since epoch. + optional uint64 end_time_ms = 2; + } + + // The count of open session operations. Each instance has a specific error + // code associated with it. + repeated Counter open_session_counter = 1; + + // The count of close session operations. Each instance has a specific error + // code associated with it. + repeated Counter close_session_counter = 2; + + // Count and execution time of getKeyRequest calls. + repeated DistributionMetric get_key_request_time_us = 3; + + // Count and execution time of provideKeyResponse calls. + repeated DistributionMetric provide_key_response_time_us = 4; + + // Count of getProvisionRequest calls. + repeated Counter get_provisioning_request_counter = 5; + + // Count of provideProvisionResponse calls. + repeated Counter provide_provisioning_response_counter = 6; + + // Count of key status events broken out by status type. + repeated Counter key_status_change_counter = 7; + + // Count of events broken out by event type + repeated Counter event_callback_counter = 8; + + // Count getPropertyByteArray calls to retrieve the device unique id. + repeated Counter get_device_unique_id_counter = 9; + + // Session ids to lifetime (start and end time) map. + // Session ids are strings of hex-encoded byte arrays. + map<string, SessionLifetime> session_lifetimes = 10; +} + diff --git a/drm/libmediadrm/protos/plugin_metrics.proto b/drm/libmediadrm/protos/plugin_metrics.proto deleted file mode 100644 index 7e3bcf5304..0000000000 --- a/drm/libmediadrm/protos/plugin_metrics.proto +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -syntax = "proto2"; - -package android.drm_metrics; - -// need this if we are using libprotobuf-cpp-2.3.0-lite -option optimize_for = LITE_RUNTIME; - -// The MetricsGroup is a collection of metric name/value pair instances -// that can be serialized and provided to a caller. -message MetricsGroup { - message Metric { - message MetricValue { - // Exactly one of the following values must be set. - optional int64 int_value = 1; - optional double double_value = 2; - optional string string_value = 3; - } - - // The name of the metric. Must be valid UTF-8. Required. - optional string name = 1; - - // The value of the metric. Required. - optional MetricValue value = 2; - } - - // The list of name/value pairs of metrics. - repeated Metric metric = 1; - - // Allow multiple sub groups of metrics. - repeated MetricsGroup metric_sub_group = 2; - - // Name of the application package associated with the metrics. - optional string app_package_name = 3; -} diff --git a/drm/libmediadrm/tests/Android.bp b/drm/libmediadrm/tests/Android.bp new file mode 100644 index 0000000000..66c906f2e9 --- /dev/null +++ b/drm/libmediadrm/tests/Android.bp @@ -0,0 +1,53 @@ +// Build definitions for unit tests. + +cc_test { + name: "CounterMetric_test", + srcs: ["CounterMetric_test.cpp"], + shared_libs: ["libmediadrm"], + include_dirs: ["frameworks/av/include/media"], + cflags: [ + "-Werror", + "-Wall", + ], +} + +cc_test { + name: "DrmMetrics_test", + srcs: ["DrmMetrics_test.cpp"], + shared_libs: [ + "android.hardware.drm@1.0", + "android.hardware.drm@1.1", + "libbinder", + "libhidlbase", + "liblog", + "libmediadrmmetrics_full", + "libmediametrics", + "libprotobuf-cpp-full", + "libutils", + ], + static_libs: ["libgmock"], + include_dirs: [ + "frameworks/av/include/media", + ], + cflags: [ + // Suppress unused parameter and no error options. These cause problems + // when using the map type in a proto definition. + "-Wno-unused-parameter", + "-Wno-error", + ] +} + +cc_test { + name: "EventMetric_test", + srcs: ["EventMetric_test.cpp"], + shared_libs: [ + "liblog", + "libmediadrm", + "libutils", + ], + include_dirs: ["frameworks/av/include/media"], + cflags: [ + "-Werror", + "-Wall", + ], +} diff --git a/drm/libmediadrm/tests/CounterMetric_test.cpp b/drm/libmediadrm/tests/CounterMetric_test.cpp new file mode 100644 index 0000000000..6bca0dafe2 --- /dev/null +++ b/drm/libmediadrm/tests/CounterMetric_test.cpp @@ -0,0 +1,80 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include "CounterMetric.h" + +namespace android { + +/** + * Unit tests for the CounterMetric class. + */ +class CounterMetricTest : public ::testing::Test { +}; + +TEST_F(CounterMetricTest, IntDataTypeEmpty) { + CounterMetric<int> metric("MyMetricName", "MetricAttributeName"); + + std::map<int, int64_t> values; + + metric.ExportValues( + [&] (int attribute_value, int64_t value) { + values[attribute_value] = value; + }); + + EXPECT_TRUE(values.empty()); +} + +TEST_F(CounterMetricTest, IntDataType) { + CounterMetric<int> metric("MyMetricName", "MetricAttributeName"); + + std::map<int, int64_t> values; + + metric.Increment(7); + metric.Increment(8); + metric.Increment(8); + + metric.ExportValues( + [&] (int attribute_value, int64_t value) { + values[attribute_value] = value; + }); + + ASSERT_EQ(2u, values.size()); + EXPECT_EQ(1, values[7]); + EXPECT_EQ(2, values[8]); +} + +TEST_F(CounterMetricTest, StringDataType) { + CounterMetric<std::string> metric("MyMetricName", "MetricAttributeName"); + + std::map<std::string, int64_t> values; + + metric.Increment("a"); + metric.Increment("b"); + metric.Increment("b"); + + metric.ExportValues( + [&] (std::string attribute_value, int64_t value) { + values[attribute_value] = value; + }); + + ASSERT_EQ(2u, values.size()); + EXPECT_EQ(1, values["a"]); + EXPECT_EQ(2, values["b"]); +} + +} // namespace android diff --git a/drm/libmediadrm/tests/DrmMetrics_test.cpp b/drm/libmediadrm/tests/DrmMetrics_test.cpp new file mode 100644 index 0000000000..64aa9d0c99 --- /dev/null +++ b/drm/libmediadrm/tests/DrmMetrics_test.cpp @@ -0,0 +1,475 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "DrmMetricsTest" +#include "mediadrm/DrmMetrics.h" + +#include <android/hardware/drm/1.0/types.h> +#include <android/hardware/drm/1.1/types.h> +#include <binder/PersistableBundle.h> +#include <google/protobuf/text_format.h> +#include <google/protobuf/util/message_differencer.h> +#include <gtest/gtest.h> +#include <utils/Log.h> + +#include "protos/metrics.pb.h" + +using ::android::drm_metrics::DrmFrameworkMetrics; +using ::android::hardware::hidl_vec; +using ::android::hardware::drm::V1_0::EventType; +using ::android::hardware::drm::V1_0::KeyStatusType; +using ::android::hardware::drm::V1_0::Status; +using ::android::hardware::drm::V1_1::DrmMetricGroup; +using ::android::os::PersistableBundle; +using ::google::protobuf::util::MessageDifferencer; +using ::google::protobuf::TextFormat; + +namespace android { + +/** + * Unit tests for the MediaDrmMetrics class. + */ +class MediaDrmMetricsTest : public ::testing::Test {}; + +/** + * This derived class mocks the clock for testing purposes. + */ +class FakeMediaDrmMetrics : public MediaDrmMetrics { + public: + FakeMediaDrmMetrics() : MediaDrmMetrics(), time_(0) {}; + + int64_t GetCurrentTimeMs() { return time_++; } + int64_t time_; +}; + +TEST_F(MediaDrmMetricsTest, EmptySuccess) { + MediaDrmMetrics metrics; + PersistableBundle bundle; + + metrics.Export(&bundle); + EXPECT_TRUE(bundle.empty()); +} + +TEST_F(MediaDrmMetricsTest, AllValuesSuccessCounts) { + MediaDrmMetrics metrics; + + metrics.mOpenSessionCounter.Increment(OK); + metrics.mCloseSessionCounter.Increment(OK); + + { + EventTimer<status_t> get_key_request_timer(&metrics.mGetKeyRequestTimeUs); + EventTimer<status_t> provide_key_response_timer( + &metrics.mProvideKeyResponseTimeUs); + get_key_request_timer.SetAttribute(OK); + provide_key_response_timer.SetAttribute(OK); + } + + metrics.mGetProvisionRequestCounter.Increment(OK); + metrics.mProvideProvisionResponseCounter.Increment(OK); + metrics.mGetDeviceUniqueIdCounter.Increment(OK); + + metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::USABLE); + metrics.mEventCounter.Increment(EventType::PROVISION_REQUIRED); + + PersistableBundle bundle; + + metrics.Export(&bundle); + EXPECT_EQ(11U, bundle.size()); + + // Verify the list of pairs of int64 metrics. + std::vector<std::pair<std::string, int64_t>> expected_values = { + { "drm.mediadrm.open_session.ok.count", 1 }, + { "drm.mediadrm.close_session.ok.count", 1 }, + { "drm.mediadrm.get_key_request.ok.count", 1 }, + { "drm.mediadrm.provide_key_response.ok.count", 1 }, + { "drm.mediadrm.get_provision_request.ok.count", 1 }, + { "drm.mediadrm.provide_provision_response.ok.count", 1 }, + { "drm.mediadrm.key_status_change.USABLE.count", 1 }, + { "drm.mediadrm.event.PROVISION_REQUIRED.count", 1 }, + { "drm.mediadrm.get_device_unique_id.ok.count", 1 }}; + for (const auto& expected_pair : expected_values) { + String16 key(expected_pair.first.c_str()); + int64_t value = -1; + EXPECT_TRUE(bundle.getLong(key, &value)) + << "Unexpected error retrieviing key: " << key; + EXPECT_EQ(expected_pair.second, value) + << "Unexpected value for " << expected_pair.first << ". " << value; + } + + // Validate timing values exist. + String16 get_key_request_key( + "drm.mediadrm.get_key_request.ok.average_time_micros"); + String16 provide_key_response_key( + "drm.mediadrm.provide_key_response.ok.average_time_micros"); + int64_t value = -1; + EXPECT_TRUE(bundle.getLong(get_key_request_key, &value)); + EXPECT_GE(value, 0); + value = -1; + EXPECT_TRUE(bundle.getLong(provide_key_response_key, &value)); + EXPECT_GE(value, 0); +} + +TEST_F(MediaDrmMetricsTest, AllValuesFull) { + MediaDrmMetrics metrics; + + metrics.mOpenSessionCounter.Increment(OK); + metrics.mOpenSessionCounter.Increment(UNEXPECTED_NULL); + + metrics.mCloseSessionCounter.Increment(OK); + metrics.mCloseSessionCounter.Increment(UNEXPECTED_NULL); + + for (status_t s : {OK, UNEXPECTED_NULL}) { + { + EventTimer<status_t> get_key_request_timer(&metrics.mGetKeyRequestTimeUs); + EventTimer<status_t> provide_key_response_timer( + &metrics.mProvideKeyResponseTimeUs); + get_key_request_timer.SetAttribute(s); + provide_key_response_timer.SetAttribute(s); + } + } + + metrics.mGetProvisionRequestCounter.Increment(OK); + metrics.mGetProvisionRequestCounter.Increment(UNEXPECTED_NULL); + metrics.mProvideProvisionResponseCounter.Increment(OK); + metrics.mProvideProvisionResponseCounter.Increment(UNEXPECTED_NULL); + metrics.mGetDeviceUniqueIdCounter.Increment(OK); + metrics.mGetDeviceUniqueIdCounter.Increment(UNEXPECTED_NULL); + + metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::USABLE); + metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::EXPIRED); + metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::OUTPUTNOTALLOWED); + metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::STATUSPENDING); + metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::INTERNALERROR); + metrics.mEventCounter.Increment(EventType::PROVISION_REQUIRED); + metrics.mEventCounter.Increment(EventType::KEY_NEEDED); + metrics.mEventCounter.Increment(EventType::KEY_EXPIRED); + metrics.mEventCounter.Increment(EventType::VENDOR_DEFINED); + metrics.mEventCounter.Increment(EventType::SESSION_RECLAIMED); + + android::Vector<uint8_t> sessionId1; + sessionId1.push_back(1); + sessionId1.push_back(2); + android::Vector<uint8_t> sessionId2; + sessionId2.push_back(3); + sessionId2.push_back(4); + String16 hexSessionId1("0102"); + String16 hexSessionId2("0304"); + + metrics.SetSessionStart(sessionId1); + metrics.SetSessionStart(sessionId2); + metrics.SetSessionEnd(sessionId2); + metrics.SetSessionEnd(sessionId1); + + PersistableBundle bundle; + metrics.Export(&bundle); + EXPECT_EQ(35U, bundle.size()); + + // Verify the list of pairs of int64 metrics. + std::vector<std::pair<std::string, int64_t>> expected_values = { + { "drm.mediadrm.open_session.ok.count", 1 }, + { "drm.mediadrm.close_session.ok.count", 1 }, + { "drm.mediadrm.get_key_request.ok.count", 1 }, + { "drm.mediadrm.provide_key_response.ok.count", 1 }, + { "drm.mediadrm.get_provision_request.ok.count", 1 }, + { "drm.mediadrm.provide_provision_response.ok.count", 1 }, + { "drm.mediadrm.get_device_unique_id.ok.count", 1 }, + { "drm.mediadrm.open_session.error.count", 1 }, + { "drm.mediadrm.close_session.error.count", 1 }, + { "drm.mediadrm.get_key_request.error.count", 1 }, + { "drm.mediadrm.provide_key_response.error.count", 1 }, + { "drm.mediadrm.get_provision_request.error.count", 1 }, + { "drm.mediadrm.provide_provision_response.error.count", 1 }, + { "drm.mediadrm.get_device_unique_id.error.count", 1 }, + { "drm.mediadrm.key_status_change.USABLE.count", 1 }, + { "drm.mediadrm.key_status_change.EXPIRED.count", 1 }, + { "drm.mediadrm.key_status_change.OUTPUT_NOT_ALLOWED.count", 1 }, + { "drm.mediadrm.key_status_change.STATUS_PENDING.count", 1 }, + { "drm.mediadrm.key_status_change.INTERNAL_ERROR.count", 1 }, + { "drm.mediadrm.event.PROVISION_REQUIRED.count", 1 }, + { "drm.mediadrm.event.KEY_NEEDED.count", 1 }, + { "drm.mediadrm.event.KEY_EXPIRED.count", 1 }, + { "drm.mediadrm.event.VENDOR_DEFINED.count", 1 }, + { "drm.mediadrm.event.SESSION_RECLAIMED.count", 1 }}; + for (const auto& expected_pair : expected_values) { + String16 key(expected_pair.first.c_str()); + int64_t value = -1; + EXPECT_TRUE(bundle.getLong(key, &value)) + << "Unexpected error retrieviing key: " << key; + EXPECT_EQ(expected_pair.second, value) + << "Unexpected value for " << expected_pair.first << ". " << value; + } + + // Verify the error lists + std::vector<std::pair<std::string, std::vector<int64_t>>> expected_vector_values = { + { "drm.mediadrm.close_session.error.list", { UNEXPECTED_NULL } }, + { "drm.mediadrm.get_device_unique_id.error.list", { UNEXPECTED_NULL } }, + { "drm.mediadrm.get_key_request.error.list", { UNEXPECTED_NULL } }, + { "drm.mediadrm.get_provision_request.error.list", { UNEXPECTED_NULL } }, + { "drm.mediadrm.open_session.error.list", { UNEXPECTED_NULL } }, + { "drm.mediadrm.provide_key_response.error.list", { UNEXPECTED_NULL } }, + { "drm.mediadrm.provide_provision_response.error.list", { UNEXPECTED_NULL } }}; + for (const auto& expected_pair : expected_vector_values) { + String16 key(expected_pair.first.c_str()); + std::vector<int64_t> values; + EXPECT_TRUE(bundle.getLongVector(key, &values)) + << "Unexpected error retrieviing key: " << key; + for (auto expected : expected_pair.second) { + EXPECT_TRUE(std::find(values.begin(), values.end(), expected) != values.end()) + << "Could not find " << expected << " for key " << expected_pair.first; + } + } + + // Verify the lifespans + PersistableBundle start_times; + PersistableBundle end_times; + String16 start_time_key("drm.mediadrm.session_start_times_ms"); + String16 end_time_key("drm.mediadrm.session_end_times_ms"); + ASSERT_TRUE(bundle.getPersistableBundle(start_time_key, &start_times)); + ASSERT_TRUE(bundle.getPersistableBundle(end_time_key, &end_times)); + EXPECT_EQ(2U, start_times.size()); + EXPECT_EQ(2U, end_times.size()); + int64_t start_time, end_time; + for (const auto& sid : { hexSessionId1, hexSessionId2 }) { + start_time = -1; + end_time = -1; + EXPECT_TRUE(start_times.getLong(sid, &start_time)); + EXPECT_TRUE(end_times.getLong(sid, &end_time)); + EXPECT_GT(start_time, 0); + EXPECT_GE(end_time, start_time); + } + + // Validate timing values exist. + String16 get_key_request_key( + "drm.mediadrm.get_key_request.ok.average_time_micros"); + String16 provide_key_response_key( + "drm.mediadrm.provide_key_response.ok.average_time_micros"); + int64_t value = -1; + EXPECT_TRUE(bundle.getLong(get_key_request_key, &value)); + EXPECT_GE(value, 0); + value = -1; + EXPECT_TRUE(bundle.getLong(provide_key_response_key, &value)); + EXPECT_GE(value, 0); +} + + +TEST_F(MediaDrmMetricsTest, CounterValuesProtoSerialization) { + MediaDrmMetrics metrics; + + metrics.mOpenSessionCounter.Increment(OK); + metrics.mOpenSessionCounter.Increment(UNEXPECTED_NULL); + metrics.mCloseSessionCounter.Increment(OK); + metrics.mCloseSessionCounter.Increment(UNEXPECTED_NULL); + + metrics.mGetProvisionRequestCounter.Increment(OK); + metrics.mGetProvisionRequestCounter.Increment(UNEXPECTED_NULL); + metrics.mProvideProvisionResponseCounter.Increment(OK); + metrics.mProvideProvisionResponseCounter.Increment(UNEXPECTED_NULL); + metrics.mGetDeviceUniqueIdCounter.Increment(OK); + metrics.mGetDeviceUniqueIdCounter.Increment(UNEXPECTED_NULL); + + metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::USABLE); + metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::EXPIRED); + metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::OUTPUTNOTALLOWED); + metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::STATUSPENDING); + metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::INTERNALERROR); + metrics.mEventCounter.Increment(EventType::PROVISION_REQUIRED); + metrics.mEventCounter.Increment(EventType::KEY_NEEDED); + metrics.mEventCounter.Increment(EventType::KEY_EXPIRED); + metrics.mEventCounter.Increment(EventType::VENDOR_DEFINED); + metrics.mEventCounter.Increment(EventType::SESSION_RECLAIMED); + + std::string serializedMetrics; + ASSERT_EQ(OK, metrics.GetSerializedMetrics(&serializedMetrics)); + + DrmFrameworkMetrics metricsProto; + ASSERT_TRUE(metricsProto.ParseFromString(serializedMetrics)); + + std::string expectedMetrics = + "open_session_counter { count: 1 attributes { error_code: -0x7FFFFFF8 } } " + "open_session_counter { count: 1 attributes { error_code: 0 } } " + "close_session_counter { count: 1 attributes { error_code: -0x7FFFFFF8 } } " + "close_session_counter { count: 1 attributes { error_code: 0 } } " + "get_provisioning_request_counter { count: 1 attributes { error_code: -0x7FFFFFF8 } } " + "get_provisioning_request_counter { count: 1 attributes { error_code: 0 } } " + "provide_provisioning_response_counter { count: 1 attributes { error_code: -0x7ffffff8 } } " + "provide_provisioning_response_counter { count: 1 attributes { error_code: 0 } } " + "get_device_unique_id_counter { count: 1 attributes { error_code: -0x7ffffff8 } } " + "get_device_unique_id_counter { count: 1 attributes { error_code: 0 } } " + "key_status_change_counter { count: 1 attributes { key_status_type: 0 } } " + "key_status_change_counter { count: 1 attributes { key_status_type: 1 } } " + "key_status_change_counter { count: 1 attributes { key_status_type: 2 } } " + "key_status_change_counter { count: 1 attributes { key_status_type: 3 } } " + "key_status_change_counter { count: 1 attributes { key_status_type: 4 } } " + "event_callback_counter { count: 1 attributes { event_type: 0 } } " + "event_callback_counter { count: 1 attributes { event_type: 1 } } " + "event_callback_counter { count: 1 attributes { event_type: 2 } } " + "event_callback_counter { count: 1 attributes { event_type: 3 } } " + "event_callback_counter { count: 1 attributes { event_type: 4 } } "; + + DrmFrameworkMetrics expectedMetricsProto; + ASSERT_TRUE(TextFormat::MergeFromString(expectedMetrics, &expectedMetricsProto)); + + std::string diffString; + MessageDifferencer differ; + differ.ReportDifferencesToString(&diffString); + ASSERT_TRUE(differ.Compare(expectedMetricsProto, metricsProto)) + << diffString; +} + +TEST_F(MediaDrmMetricsTest, TimeMetricsProtoSerialization) { + MediaDrmMetrics metrics; + + for (status_t s : {OK, UNEXPECTED_NULL}) { + double time = 0; + for (int i = 0; i < 5; i++) { + time += 1.0; + metrics.mGetKeyRequestTimeUs.Record(time, s); + metrics.mProvideKeyResponseTimeUs.Record(time, s); + } + } + + std::string serializedMetrics; + ASSERT_EQ(OK, metrics.GetSerializedMetrics(&serializedMetrics)); + + DrmFrameworkMetrics metricsProto; + ASSERT_TRUE(metricsProto.ParseFromString(serializedMetrics)); + + std::string expectedMetrics = + "get_key_request_time_us { " + " min: 1 max: 5 mean: 3.5 variance: 1 operation_count: 5 " + " attributes { error_code: -0x7FFFFFF8 } " + "} " + "get_key_request_time_us { " + " min: 1 max: 5 mean: 3.5 variance: 1 operation_count: 5 " + " attributes { error_code: 0 } " + "} " + "provide_key_response_time_us { " + " min: 1 max: 5 mean: 3.5 variance: 1 operation_count: 5 " + " attributes { error_code: -0x7FFFFFF8 } " + "} " + "provide_key_response_time_us { " + " min: 1 max: 5 mean: 3.5 variance: 1 operation_count: 5 " + " attributes { error_code: 0 } " + "} "; + + DrmFrameworkMetrics expectedMetricsProto; + ASSERT_TRUE(TextFormat::MergeFromString(expectedMetrics, &expectedMetricsProto)); + + std::string diffString; + MessageDifferencer differ; + differ.ReportDifferencesToString(&diffString); + ASSERT_TRUE(differ.Compare(expectedMetricsProto, metricsProto)) + << diffString; +} + +TEST_F(MediaDrmMetricsTest, SessionLifetimeProtoSerialization) { + // Use the fake so the clock is predictable; + FakeMediaDrmMetrics metrics; + + android::Vector<uint8_t> sessionId1; + sessionId1.push_back(1); + sessionId1.push_back(2); + android::Vector<uint8_t> sessionId2; + sessionId2.push_back(3); + sessionId2.push_back(4); + + metrics.SetSessionStart(sessionId1); + metrics.SetSessionStart(sessionId2); + metrics.SetSessionEnd(sessionId2); + metrics.SetSessionEnd(sessionId1); + + std::string serializedMetrics; + ASSERT_EQ(OK, metrics.GetSerializedMetrics(&serializedMetrics)); + + DrmFrameworkMetrics metricsProto; + ASSERT_TRUE(metricsProto.ParseFromString(serializedMetrics)); + + std::string expectedMetrics = + "session_lifetimes: { " + " key: '0102' " + " value { start_time_ms: 0 end_time_ms: 3 } " + "} " + "session_lifetimes: { " + " key: '0304' " + " value { start_time_ms: 1 end_time_ms: 2 } " + "} "; + + DrmFrameworkMetrics expectedMetricsProto; + ASSERT_TRUE(TextFormat::MergeFromString(expectedMetrics, &expectedMetricsProto)); + + std::string diffString; + MessageDifferencer differ; + differ.ReportDifferencesToString(&diffString); + ASSERT_TRUE(differ.Compare(expectedMetricsProto, metricsProto)) + << diffString; +} + +TEST_F(MediaDrmMetricsTest, HidlToBundleMetricsEmpty) { + hidl_vec<DrmMetricGroup> hidlMetricGroups; + PersistableBundle bundleMetricGroups; + + ASSERT_EQ(OK, MediaDrmMetrics::HidlMetricsToBundle(hidlMetricGroups, &bundleMetricGroups)); + ASSERT_EQ(0U, bundleMetricGroups.size()); +} + +TEST_F(MediaDrmMetricsTest, HidlToBundleMetricsMultiple) { + DrmMetricGroup hidlMetricGroup = + { { { + "open_session_ok", + { { "status", DrmMetricGroup::ValueType::INT64_TYPE, + (int64_t) Status::OK, 0.0, "" } }, + { { "count", DrmMetricGroup::ValueType::INT64_TYPE, 3, 0.0, "" } } + }, + { + "close_session_not_opened", + { { "status", DrmMetricGroup::ValueType::INT64_TYPE, + (int64_t) Status::ERROR_DRM_SESSION_NOT_OPENED, 0.0, "" } }, + { { "count", DrmMetricGroup::ValueType::INT64_TYPE, 7, 0.0, "" } } + } } }; + + PersistableBundle bundleMetricGroups; + ASSERT_EQ(OK, MediaDrmMetrics::HidlMetricsToBundle(hidl_vec<DrmMetricGroup>({hidlMetricGroup}), + &bundleMetricGroups)); + ASSERT_EQ(1U, bundleMetricGroups.size()); + PersistableBundle bundleMetricGroup; + ASSERT_TRUE(bundleMetricGroups.getPersistableBundle(String16("[0]"), &bundleMetricGroup)); + ASSERT_EQ(2U, bundleMetricGroup.size()); + + // Verify each metric. + PersistableBundle metric; + ASSERT_TRUE(bundleMetricGroup.getPersistableBundle(String16("open_session_ok"), &metric)); + PersistableBundle metricInstance; + ASSERT_TRUE(metric.getPersistableBundle(String16("[0]"), &metricInstance)); + int64_t value = 0; + ASSERT_TRUE(metricInstance.getLong(String16("count"), &value)); + ASSERT_EQ(3, value); + PersistableBundle attributeBundle; + ASSERT_TRUE(metricInstance.getPersistableBundle(String16("attributes"), &attributeBundle)); + ASSERT_TRUE(attributeBundle.getLong(String16("status"), &value)); + ASSERT_EQ((int64_t) Status::OK, value); + + ASSERT_TRUE(bundleMetricGroup.getPersistableBundle(String16("close_session_not_opened"), + &metric)); + ASSERT_TRUE(metric.getPersistableBundle(String16("[0]"), &metricInstance)); + ASSERT_TRUE(metricInstance.getLong(String16("count"), &value)); + ASSERT_EQ(7, value); + ASSERT_TRUE(metricInstance.getPersistableBundle(String16("attributes"), &attributeBundle)); + value = 0; + ASSERT_TRUE(attributeBundle.getLong(String16("status"), &value)); + ASSERT_EQ((int64_t) Status::ERROR_DRM_SESSION_NOT_OPENED, value); +} + +} // namespace android diff --git a/drm/libmediadrm/tests/EventMetric_test.cpp b/drm/libmediadrm/tests/EventMetric_test.cpp new file mode 100644 index 0000000000..eb6c4f684b --- /dev/null +++ b/drm/libmediadrm/tests/EventMetric_test.cpp @@ -0,0 +1,142 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include "EventMetric.h" + +namespace android { + +/** + * Unit tests for the EventMetric class. + */ + +TEST(EventMetricTest, IntDataTypeEmpty) { + EventMetric<int> metric("MyMetricName", "MetricAttributeName"); + + std::map<int, EventStatistics> values; + + metric.ExportValues( + [&] (int attribute_value, const EventStatistics& value) { + values[attribute_value] = value; + }); + + EXPECT_TRUE(values.empty()); +} + +TEST(EventMetricTest, IntDataType) { + EventMetric<int> metric("MyMetricName", "MetricAttributeName"); + + std::map<int, EventStatistics> values; + + metric.Record(4, 7); + metric.Record(5, 8); + metric.Record(5, 8); + metric.Record(5, 8); + metric.Record(6, 8); + metric.Record(6, 8); + metric.Record(6, 8); + + metric.ExportValues( + [&] (int attribute_value, const EventStatistics& value) { + values[attribute_value] = value; + }); + + ASSERT_EQ(2u, values.size()); + EXPECT_EQ(4, values[7].min); + EXPECT_EQ(4, values[7].max); + EXPECT_EQ(4, values[7].mean); + EXPECT_EQ(1, values[7].count); + + EXPECT_EQ(5, values[8].min); + EXPECT_EQ(6, values[8].max); + // This is an approximate value because of the technique we're using. + EXPECT_NEAR(5.5, values[8].mean, 0.2); + EXPECT_EQ(6, values[8].count); +} + +TEST(EventMetricTest, StringDataType) { + EventMetric<std::string> metric("MyMetricName", "MetricAttributeName"); + + std::map<std::string, EventStatistics> values; + + metric.Record(1, "a"); + metric.Record(2, "b"); + metric.Record(2, "b"); + metric.Record(3, "b"); + metric.Record(3, "b"); + + metric.ExportValues( + [&] (std::string attribute_value, const EventStatistics& value) { + values[attribute_value] = value; + }); + + ASSERT_EQ(2u, values.size()); + EXPECT_EQ(1, values["a"].min); + EXPECT_EQ(1, values["a"].max); + EXPECT_EQ(1, values["a"].mean); + EXPECT_EQ(1, values["a"].count); + + EXPECT_EQ(2, values["b"].min); + EXPECT_EQ(3, values["b"].max); + EXPECT_NEAR(2.5, values["b"].mean, 0.2); + EXPECT_EQ(4, values["b"].count); +} + +// Helper class that allows us to mock the clock. +template<typename AttributeType> +class MockEventTimer : public EventTimer<AttributeType> { + public: + explicit MockEventTimer(nsecs_t time_delta_ns, + EventMetric<AttributeType>* metric) + : EventTimer<AttributeType>(metric) { + // Pretend the event started earlier. + this->start_time_ = systemTime() - time_delta_ns; + } +}; + +TEST(EventTimerTest, IntDataType) { + EventMetric<int> metric("MyMetricName", "MetricAttributeName"); + + for (int i = 0; i < 5; i++) { + { + // Add a mock time delta. + MockEventTimer<int> metric_timer(i * 1000000, &metric); + metric_timer.SetAttribute(i % 2); + } + } + + std::map<int, EventStatistics> values; + metric.ExportValues( + [&] (int attribute_value, const EventStatistics& value) { + values[attribute_value] = value; + }); + + ASSERT_EQ(2u, values.size()); + EXPECT_LT(values[0].min, values[0].max); + EXPECT_GE(4000, values[0].max); + EXPECT_GT(values[0].mean, values[0].min); + EXPECT_LE(values[0].mean, values[0].max); + EXPECT_EQ(3, values[0].count); + + EXPECT_LT(values[1].min, values[1].max); + EXPECT_GE(3000, values[1].max); + EXPECT_GT(values[1].mean, values[1].min); + EXPECT_LE(values[1].mean, values[1].max); + EXPECT_EQ(2, values[1].count); +} + +} // namespace android diff --git a/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp b/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp index e27631fc53..73ed8c3637 100644 --- a/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp +++ b/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.cpp @@ -55,7 +55,7 @@ status_t ClearKeyCasFactory::queryPlugins( status_t ClearKeyCasFactory::createPlugin( int32_t CA_system_id, - uint64_t appData, + void *appData, CasPluginCallback callback, CasPlugin **plugin) { if (!isSystemIdSupported(CA_system_id)) { @@ -83,7 +83,7 @@ status_t ClearKeyDescramblerFactory::createPlugin( /////////////////////////////////////////////////////////////////////////////// ClearKeyCasPlugin::ClearKeyCasPlugin( - uint64_t appData, CasPluginCallback callback) + void *appData, CasPluginCallback callback) : mCallback(callback), mAppData(appData) { ALOGV("CTOR"); } @@ -347,6 +347,9 @@ ssize_t ClearKeyCasSession::decrypt( return ERROR_CAS_CANNOT_HANDLE; } + scramblingControl = (DescramblerPlugin::ScramblingControl) + (scramblingControl & DescramblerPlugin::kScrambling_Mask_Key); + AES_KEY contentKey; if (scramblingControl != DescramblerPlugin::kScrambling_Unscrambled) { diff --git a/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.h b/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.h index b7134e4038..42cfb8f2fb 100644 --- a/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.h +++ b/drm/mediacas/plugins/clearkey/ClearKeyCasPlugin.h @@ -44,7 +44,7 @@ public: std::vector<CasPluginDescriptor> *descriptors) const override; virtual status_t createPlugin( int32_t CA_system_id, - uint64_t appData, + void *appData, CasPluginCallback callback, CasPlugin **plugin) override; }; @@ -62,7 +62,7 @@ public: class ClearKeyCasPlugin : public CasPlugin { public: - ClearKeyCasPlugin(uint64_t appData, CasPluginCallback callback); + ClearKeyCasPlugin(void *appData, CasPluginCallback callback); virtual ~ClearKeyCasPlugin(); virtual status_t setPrivateData( @@ -94,7 +94,7 @@ private: Mutex mKeyFetcherLock; std::unique_ptr<KeyFetcher> mKeyFetcher; CasPluginCallback mCallback; - uint64_t mAppData; + void* mAppData; }; class ClearKeyDescramblerPlugin : public DescramblerPlugin { diff --git a/drm/mediacas/plugins/clearkey/ClearKeyFetcher.cpp b/drm/mediacas/plugins/clearkey/ClearKeyFetcher.cpp index cb69f911ff..eaa3390d0b 100644 --- a/drm/mediacas/plugins/clearkey/ClearKeyFetcher.cpp +++ b/drm/mediacas/plugins/clearkey/ClearKeyFetcher.cpp @@ -89,7 +89,7 @@ status_t ClearKeyFetcher::ObtainKey(const sp<ABuffer>& buffer, // asset_id change. If it sends an EcmContainer with 2 Ecms with different // asset_ids (old and new) then it might be best to prefetch the Emm. if ((asset_.id() != 0) && (*asset_id != asset_.id())) { - ALOGW("Asset_id change from %" PRIu64 " to %" PRIu64, asset_.id(), *asset_id); + ALOGW("Asset_id change from %llu to %" PRIu64, asset_.id(), *asset_id); asset_.Clear(); } diff --git a/drm/mediacas/plugins/clearkey/ClearKeySessionLibrary.cpp b/drm/mediacas/plugins/clearkey/ClearKeySessionLibrary.cpp index faea00816c..4b4051d5bb 100644 --- a/drm/mediacas/plugins/clearkey/ClearKeySessionLibrary.cpp +++ b/drm/mediacas/plugins/clearkey/ClearKeySessionLibrary.cpp @@ -95,7 +95,7 @@ void ClearKeySessionLibrary::destroySession(const CasSessionId& sessionId) { void ClearKeySessionLibrary::destroyPlugin(CasPlugin *plugin) { Mutex::Autolock lock(mSessionsLock); - for (ssize_t index = mIDToSessionMap.size() - 1; index >= 0; index--) { + for (ssize_t index = (ssize_t)mIDToSessionMap.size() - 1; index >= 0; index--) { sp<ClearKeyCasSession> session = mIDToSessionMap.valueAt(index); if (session->getPlugin() == plugin) { mIDToSessionMap.removeItemsAt(index); diff --git a/drm/mediacas/plugins/clearkey/JsonAssetLoader.cpp b/drm/mediacas/plugins/clearkey/JsonAssetLoader.cpp index 6e1004caa1..ee8dba3194 100644 --- a/drm/mediacas/plugins/clearkey/JsonAssetLoader.cpp +++ b/drm/mediacas/plugins/clearkey/JsonAssetLoader.cpp @@ -36,8 +36,6 @@ const String8 kEncryptionKeyTag("encryption_key"); const String8 kCasTypeTag("cas_type"); const String8 kBase64Padding("="); -const uint32_t kKeyLength = 16; - JsonAssetLoader::JsonAssetLoader() { } diff --git a/drm/mediacas/plugins/clearkey/ecm_generator.cpp b/drm/mediacas/plugins/clearkey/ecm_generator.cpp index 7d296590e4..218ce35d98 100644 --- a/drm/mediacas/plugins/clearkey/ecm_generator.cpp +++ b/drm/mediacas/plugins/clearkey/ecm_generator.cpp @@ -38,8 +38,6 @@ const uint32_t kContentKeyByteSize = 16; const uint16_t kTotalEcmSize = kEcmClearFieldsSize + kContentKeyByteSize; // clear fields + clear key -const uint32_t kKeyLength = 16; - #define UNALIGNED_LOAD32(_p) (*reinterpret_cast<const uint32_t *>(_p)) static uint32_t Load32(const void *p) { diff --git a/drm/mediacas/plugins/clearkey/tests/ClearKeyFetcherTest.cpp b/drm/mediacas/plugins/clearkey/tests/ClearKeyFetcherTest.cpp index ace086ac00..d12cfebd70 100644 --- a/drm/mediacas/plugins/clearkey/tests/ClearKeyFetcherTest.cpp +++ b/drm/mediacas/plugins/clearkey/tests/ClearKeyFetcherTest.cpp @@ -93,7 +93,7 @@ TEST_F(ClearKeyFetcherTest, Success) { uint64_t asset_id; std::vector<KeyFetcher::KeyInfo> keys; EXPECT_EQ(OK, fetcher.ObtainKey(ecm_, &asset_id, &keys)); - EXPECT_EQ(2, keys.size()); + EXPECT_EQ(2U, keys.size()); EXPECT_EQ(0, keys[0].key_id); EXPECT_EQ(content_key_[0]->size(), keys[0].key_bytes->size()); EXPECT_EQ(0, memcmp(content_key_[0]->data(), diff --git a/drm/mediacas/plugins/mock/MockCasPlugin.cpp b/drm/mediacas/plugins/mock/MockCasPlugin.cpp index 18cd9a4cda..8404a83935 100644 --- a/drm/mediacas/plugins/mock/MockCasPlugin.cpp +++ b/drm/mediacas/plugins/mock/MockCasPlugin.cpp @@ -49,8 +49,8 @@ status_t MockCasFactory::queryPlugins( status_t MockCasFactory::createPlugin( int32_t CA_system_id, - uint64_t appData, - CasPluginCallback callback, + void* /*appData*/, + CasPluginCallback /*callback*/, CasPlugin **plugin) { if (!isSystemIdSupported(CA_system_id)) { return BAD_VALUE; @@ -98,7 +98,7 @@ MockCasPlugin::~MockCasPlugin() { MockSessionLibrary::get()->destroyPlugin(this); } -status_t MockCasPlugin::setPrivateData(const CasData &data) { +status_t MockCasPlugin::setPrivateData(const CasData& /*data*/) { ALOGV("setPrivateData"); return OK; } @@ -123,7 +123,7 @@ status_t MockCasPlugin::closeSession(const CasSessionId &sessionId) { } status_t MockCasPlugin::setSessionPrivateData( - const CasSessionId &sessionId, const CasData &data) { + const CasSessionId &sessionId, const CasData& /*data*/) { ALOGV("setSessionPrivateData: sessionId=%s", arrayToString(sessionId).string()); Mutex::Autolock lock(mLock); @@ -146,7 +146,7 @@ status_t MockCasPlugin::processEcm( if (session == NULL) { return BAD_VALUE; } - ALOGV("ECM: size=%d", ecm.size()); + ALOGV("ECM: size=%zu", ecm.size()); ALOGV("ECM: data=%s", arrayToString(ecm).string()); return OK; @@ -156,14 +156,14 @@ status_t MockCasPlugin::processEmm(const CasEmm& emm) { ALOGV("processEmm"); Mutex::Autolock lock(mLock); - ALOGV("EMM: size=%d", emm.size()); + ALOGV("EMM: size=%zu", emm.size()); ALOGV("EMM: data=%s", arrayToString(emm).string()); return OK; } status_t MockCasPlugin::sendEvent( - int32_t event, int arg, const CasData &eventData) { + int32_t event, int /*arg*/, const CasData& /*eventData*/) { ALOGV("sendEvent: event=%d", event); Mutex::Autolock lock(mLock); @@ -178,7 +178,7 @@ status_t MockCasPlugin::provision(const String8 &str) { } status_t MockCasPlugin::refreshEntitlements( - int32_t refreshType, const CasData &refreshData) { + int32_t /*refreshType*/, const CasData &refreshData) { ALOGV("refreshEntitlements: refreshData=%s", arrayToString(refreshData).string()); Mutex::Autolock lock(mLock); @@ -216,7 +216,7 @@ ssize_t MockDescramblerPlugin::descramble( int32_t srcOffset, void *dstPtr, int32_t dstOffset, - AString *errorDetailMsg) { + AString* /*errorDetailMsg*/) { ALOGV("MockDescramblerPlugin::descramble(secure=%d, sctrl=%d," "subSamples=%s, srcPtr=%p, dstPtr=%p, srcOffset=%d, dstOffset=%d)", (int)secure, (int)scramblingControl, diff --git a/drm/mediacas/plugins/mock/MockCasPlugin.h b/drm/mediacas/plugins/mock/MockCasPlugin.h index 9632492e99..81069906ed 100644 --- a/drm/mediacas/plugins/mock/MockCasPlugin.h +++ b/drm/mediacas/plugins/mock/MockCasPlugin.h @@ -39,7 +39,7 @@ public: std::vector<CasPluginDescriptor> *descriptors) const override; virtual status_t createPlugin( int32_t CA_system_id, - uint64_t appData, + void *appData, CasPluginCallback callback, CasPlugin **plugin) override; }; diff --git a/drm/mediadrm/plugins/clearkey/common/Android.bp b/drm/mediadrm/plugins/clearkey/common/Android.bp new file mode 100644 index 0000000000..2c674e1f29 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/common/Android.bp @@ -0,0 +1,38 @@ +// +// Copyright (C) 2018 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +cc_library_static { + name: "libclearkeycommon", + vendor: true, + + srcs: [ + "ClearKeyUUID.cpp", + "Utils.cpp", + ], + + cflags: ["-Wall", "-Werror"], + + include_dirs: ["frameworks/av/include"], + + shared_libs: ["libutils"], + + export_include_dirs: ["include"], + + sanitize: { + integer_overflow: true, + }, +} + diff --git a/drm/mediadrm/plugins/clearkey/ClearKeyUUID.cpp b/drm/mediadrm/plugins/clearkey/common/ClearKeyUUID.cpp index 0259a42a15..0259a42a15 100644 --- a/drm/mediadrm/plugins/clearkey/ClearKeyUUID.cpp +++ b/drm/mediadrm/plugins/clearkey/common/ClearKeyUUID.cpp diff --git a/drm/mediadrm/plugins/clearkey/Utils.cpp b/drm/mediadrm/plugins/clearkey/common/Utils.cpp index 93c643b636..93c643b636 100644 --- a/drm/mediadrm/plugins/clearkey/Utils.cpp +++ b/drm/mediadrm/plugins/clearkey/common/Utils.cpp diff --git a/drm/mediadrm/plugins/clearkey/ClearKeyUUID.h b/drm/mediadrm/plugins/clearkey/common/include/ClearKeyUUID.h index ac99418619..ac99418619 100644 --- a/drm/mediadrm/plugins/clearkey/ClearKeyUUID.h +++ b/drm/mediadrm/plugins/clearkey/common/include/ClearKeyUUID.h diff --git a/drm/mediadrm/plugins/clearkey/MimeType.h b/drm/mediadrm/plugins/clearkey/common/include/MimeType.h index 085f17a3e8..085f17a3e8 100644 --- a/drm/mediadrm/plugins/clearkey/MimeType.h +++ b/drm/mediadrm/plugins/clearkey/common/include/MimeType.h diff --git a/drm/mediadrm/plugins/clearkey/Utils.h b/drm/mediadrm/plugins/clearkey/common/include/Utils.h index 25431247e0..25431247e0 100644 --- a/drm/mediadrm/plugins/clearkey/Utils.h +++ b/drm/mediadrm/plugins/clearkey/common/include/Utils.h diff --git a/drm/mediadrm/plugins/clearkey/AesCtrDecryptor.cpp b/drm/mediadrm/plugins/clearkey/default/AesCtrDecryptor.cpp index f7106b275e..f7106b275e 100644 --- a/drm/mediadrm/plugins/clearkey/AesCtrDecryptor.cpp +++ b/drm/mediadrm/plugins/clearkey/default/AesCtrDecryptor.cpp diff --git a/drm/mediadrm/plugins/clearkey/Android.bp b/drm/mediadrm/plugins/clearkey/default/Android.bp index 2973fcff54..7ba57088cc 100644 --- a/drm/mediadrm/plugins/clearkey/Android.bp +++ b/drm/mediadrm/plugins/clearkey/default/Android.bp @@ -16,10 +16,10 @@ cc_library_shared { name: "libdrmclearkeyplugin", + vendor: true, srcs: [ "AesCtrDecryptor.cpp", - "ClearKeyUUID.cpp", "CreatePluginFactories.cpp", "CryptoFactory.cpp", "CryptoPlugin.cpp", @@ -29,12 +29,12 @@ cc_library_shared { "JsonWebKey.cpp", "Session.cpp", "SessionLibrary.cpp", - "Utils.cpp", ], - vendor: true, relative_install_path: "mediadrm", + cflags: ["-Wall", "-Werror"], + shared_libs: [ "libcrypto", "liblog", @@ -42,15 +42,23 @@ cc_library_shared { "libutils", ], - static_libs: ["libjsmn"], + static_libs: [ + "libclearkeycommon", + "libjsmn" + ], + + local_include_dirs: ["include"], + export_include_dirs: ["include"], + export_static_lib_headers: ["libjsmn"], include_dirs: [ "frameworks/native/include", "frameworks/av/include", ], - export_include_dirs: ["."], - export_static_lib_headers: ["libjsmn"], + sanitize: { + integer_overflow: true, + }, } //######################################################################## diff --git a/drm/mediadrm/plugins/clearkey/CreatePluginFactories.cpp b/drm/mediadrm/plugins/clearkey/default/CreatePluginFactories.cpp index ec1420e723..ec1420e723 100644 --- a/drm/mediadrm/plugins/clearkey/CreatePluginFactories.cpp +++ b/drm/mediadrm/plugins/clearkey/default/CreatePluginFactories.cpp diff --git a/drm/mediadrm/plugins/clearkey/CryptoFactory.cpp b/drm/mediadrm/plugins/clearkey/default/CryptoFactory.cpp index eeb64c3d34..f15f92b12e 100644 --- a/drm/mediadrm/plugins/clearkey/CryptoFactory.cpp +++ b/drm/mediadrm/plugins/clearkey/default/CryptoFactory.cpp @@ -15,7 +15,7 @@ */ //#define LOG_NDEBUG 0 -#define LOG_TAG "ClearKeyCryptoPlugin" +#define LOG_TAG "ClearKeyCryptoFactory" #include <utils/Log.h> #include <utils/Errors.h> diff --git a/drm/mediadrm/plugins/clearkey/CryptoPlugin.cpp b/drm/mediadrm/plugins/clearkey/default/CryptoPlugin.cpp index ee97976387..ee97976387 100644 --- a/drm/mediadrm/plugins/clearkey/CryptoPlugin.cpp +++ b/drm/mediadrm/plugins/clearkey/default/CryptoPlugin.cpp diff --git a/drm/mediadrm/plugins/clearkey/DrmFactory.cpp b/drm/mediadrm/plugins/clearkey/default/DrmFactory.cpp index c83321b2e6..8301e40757 100644 --- a/drm/mediadrm/plugins/clearkey/DrmFactory.cpp +++ b/drm/mediadrm/plugins/clearkey/default/DrmFactory.cpp @@ -15,7 +15,7 @@ */ //#define LOG_NDEBUG 0 -#define LOG_TAG "ClearKeyCryptoPlugin" +#define LOG_TAG "ClearKeyDrmFactory" #include <utils/Log.h> #include <utils/Errors.h> diff --git a/drm/mediadrm/plugins/clearkey/DrmPlugin.cpp b/drm/mediadrm/plugins/clearkey/default/DrmPlugin.cpp index ec07d87b1c..1b8b8c1e65 100644 --- a/drm/mediadrm/plugins/clearkey/DrmPlugin.cpp +++ b/drm/mediadrm/plugins/clearkey/default/DrmPlugin.cpp @@ -15,14 +15,14 @@ */ //#define LOG_NDEBUG 0 -#define LOG_TAG "ClearKeyCryptoPlugin" +#define LOG_TAG "ClearKeyDrmPlugin" #include <utils/Log.h> #include <media/stagefright/MediaErrors.h> #include <utils/StrongPointer.h> #include "DrmPlugin.h" - +#include "ClearKeyDrmProperties.h" #include "Session.h" namespace { @@ -44,7 +44,22 @@ using android::sp; DrmPlugin::DrmPlugin(SessionLibrary* sessionLibrary) : mSessionLibrary(sessionLibrary) { + mPlayPolicy.clear(); + initProperties(); +} + +void DrmPlugin::initProperties() { + mStringProperties.clear(); + mStringProperties.add(kVendorKey, kVendorValue); + mStringProperties.add(kVersionKey, kVersionValue); + mStringProperties.add(kPluginDescriptionKey, kPluginDescriptionValue); + mStringProperties.add(kAlgorithmsKey, kAlgorithmsValue); + mStringProperties.add(kListenerTestSupportKey, kListenerTestSupportValue); + + Vector<uint8_t> testDeviceId; + testDeviceId.appendArray(kTestDeviceIdData, sizeof(kTestDeviceIdData) / sizeof(uint8_t)); + mByteArrayProperties.add(kDeviceIdKey, testDeviceId); } status_t DrmPlugin::openSession(Vector<uint8_t>& sessionId) { @@ -122,22 +137,63 @@ status_t DrmPlugin::provideKeyResponse( return res; } +status_t DrmPlugin::getPropertyByteArray( + const String8& name, Vector<uint8_t>& value) const { + ssize_t index = mByteArrayProperties.indexOfKey(name); + if (index < 0) { + ALOGE("App requested unknown property: %s", name.string()); + return android::ERROR_DRM_CANNOT_HANDLE; + } + value = mByteArrayProperties.valueAt(index); + return android::OK; +} + +status_t DrmPlugin::setPropertyByteArray( + const String8& name, const Vector<uint8_t>& value) +{ + UNUSED(value); + if (0 == name.compare(kDeviceIdKey)) { + ALOGD("Cannot set immutable property: %s", name.string()); + return android::ERROR_DRM_CANNOT_HANDLE; + } + + // Setting of undefined properties is not supported + ALOGE("Failed to set property byte array, key=%s", name.string()); + return android::ERROR_DRM_CANNOT_HANDLE; +} + status_t DrmPlugin::getPropertyString( const String8& name, String8& value) const { - if (name == "vendor") { - value = "Google"; - } else if (name == "version") { - value = "1.0"; - } else if (name == "description") { - value = "ClearKey CDM"; - } else if (name == "algorithms") { - value = ""; - } else if (name == "listenerTestSupport") { - value = "true"; - } else { - ALOGE("App requested unknown string property %s", name.string()); + ssize_t index = mStringProperties.indexOfKey(name); + if (index < 0) { + ALOGE("App requested unknown property: %s", name.string()); + return android::ERROR_DRM_CANNOT_HANDLE; + } + value = mStringProperties.valueAt(index); + return android::OK; +} + +status_t DrmPlugin::setPropertyString( + const String8& name, const String8& value) { + String8 immutableKeys; + immutableKeys.appendFormat("%s,%s,%s,%s", + kAlgorithmsKey.string(), kPluginDescriptionKey.string(), + kVendorKey.string(), kVersionKey.string()); + if (immutableKeys.contains(name.string())) { + ALOGD("Cannot set immutable property: %s", name.string()); + return android::ERROR_DRM_CANNOT_HANDLE; + } + + ssize_t index = mStringProperties.indexOfKey(name); + if (index < 0) { + ALOGE("Cannot set undefined property string, key=%s", name.string()); return android::ERROR_DRM_CANNOT_HANDLE; } + + if (mStringProperties.add(name, value) < 0) { + ALOGE("Failed to set property string, key=%s", name.string()); + return android::ERROR_DRM_UNKNOWN; + } return android::OK; } diff --git a/drm/mediadrm/plugins/clearkey/InitDataParser.cpp b/drm/mediadrm/plugins/clearkey/default/InitDataParser.cpp index caff3939be..caff3939be 100644 --- a/drm/mediadrm/plugins/clearkey/InitDataParser.cpp +++ b/drm/mediadrm/plugins/clearkey/default/InitDataParser.cpp diff --git a/drm/mediadrm/plugins/clearkey/JsonWebKey.cpp b/drm/mediadrm/plugins/clearkey/default/JsonWebKey.cpp index 53ffae4ccd..53ffae4ccd 100644 --- a/drm/mediadrm/plugins/clearkey/JsonWebKey.cpp +++ b/drm/mediadrm/plugins/clearkey/default/JsonWebKey.cpp diff --git a/drm/mediadrm/plugins/clearkey/Session.cpp b/drm/mediadrm/plugins/clearkey/default/Session.cpp index d210f5e610..b3ceaeca51 100644 --- a/drm/mediadrm/plugins/clearkey/Session.cpp +++ b/drm/mediadrm/plugins/clearkey/default/Session.cpp @@ -15,7 +15,7 @@ */ //#define LOG_NDEBUG 0 -#define LOG_TAG "ClearKeyCryptoPlugin" +#define LOG_TAG "ClearKeySession" #include <utils/Log.h> #include <media/stagefright/MediaErrors.h> diff --git a/drm/mediadrm/plugins/clearkey/SessionLibrary.cpp b/drm/mediadrm/plugins/clearkey/default/SessionLibrary.cpp index 0419f9776a..529230ea74 100644 --- a/drm/mediadrm/plugins/clearkey/SessionLibrary.cpp +++ b/drm/mediadrm/plugins/clearkey/default/SessionLibrary.cpp @@ -15,7 +15,7 @@ */ //#define LOG_NDEBUG 0 -#define LOG_TAG "ClearKeyCryptoPlugin" +#define LOG_TAG "ClearKeySessionLibrary" #include <utils/Log.h> #include <utils/String8.h> diff --git a/drm/mediadrm/plugins/clearkey/AesCtrDecryptor.h b/drm/mediadrm/plugins/clearkey/default/include/AesCtrDecryptor.h index edb84458a7..edb84458a7 100644 --- a/drm/mediadrm/plugins/clearkey/AesCtrDecryptor.h +++ b/drm/mediadrm/plugins/clearkey/default/include/AesCtrDecryptor.h diff --git a/drm/mediadrm/plugins/clearkey/default/include/ClearKeyDrmProperties.h b/drm/mediadrm/plugins/clearkey/default/include/ClearKeyDrmProperties.h new file mode 100644 index 0000000000..a99e17447e --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/default/include/ClearKeyDrmProperties.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CLEARKEY_DRM_PROPERTIES_H_ +#define CLEARKEY_DRM_PROPERTIES_H_ + +#include <utils/String8.h> + +namespace clearkeydrm { + +static const android::String8 kVendorKey("vendor"); +static const android::String8 kVendorValue("Google"); +static const android::String8 kVersionKey("version"); +static const android::String8 kVersionValue("1.0"); +static const android::String8 kPluginDescriptionKey("description"); +static const android::String8 kPluginDescriptionValue("ClearKey CDM"); +static const android::String8 kAlgorithmsKey("algorithms"); +static const android::String8 kAlgorithmsValue(""); +static const android::String8 kListenerTestSupportKey("listenerTestSupport"); +static const android::String8 kListenerTestSupportValue("true"); + +static const android::String8 kDeviceIdKey("deviceId"); +static const uint8_t kTestDeviceIdData[] = + {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}; +} // namespace clearkeydrm + +#endif // CLEARKEY_DRM_PROPERTIES_H_ diff --git a/drm/mediadrm/plugins/clearkey/ClearKeyTypes.h b/drm/mediadrm/plugins/clearkey/default/include/ClearKeyTypes.h index a28959ae5a..a28959ae5a 100644 --- a/drm/mediadrm/plugins/clearkey/ClearKeyTypes.h +++ b/drm/mediadrm/plugins/clearkey/default/include/ClearKeyTypes.h diff --git a/drm/mediadrm/plugins/clearkey/CreatePluginFactories.h b/drm/mediadrm/plugins/clearkey/default/include/CreatePluginFactories.h index d9acec1ad6..d9acec1ad6 100644 --- a/drm/mediadrm/plugins/clearkey/CreatePluginFactories.h +++ b/drm/mediadrm/plugins/clearkey/default/include/CreatePluginFactories.h diff --git a/drm/mediadrm/plugins/clearkey/CryptoFactory.h b/drm/mediadrm/plugins/clearkey/default/include/CryptoFactory.h index 568bc4b0e4..568bc4b0e4 100644 --- a/drm/mediadrm/plugins/clearkey/CryptoFactory.h +++ b/drm/mediadrm/plugins/clearkey/default/include/CryptoFactory.h diff --git a/drm/mediadrm/plugins/clearkey/CryptoPlugin.h b/drm/mediadrm/plugins/clearkey/default/include/CryptoPlugin.h index a6d2f7be71..a6d2f7be71 100644 --- a/drm/mediadrm/plugins/clearkey/CryptoPlugin.h +++ b/drm/mediadrm/plugins/clearkey/default/include/CryptoPlugin.h diff --git a/drm/mediadrm/plugins/clearkey/DrmFactory.h b/drm/mediadrm/plugins/clearkey/default/include/DrmFactory.h index 0bc0843929..0bc0843929 100644 --- a/drm/mediadrm/plugins/clearkey/DrmFactory.h +++ b/drm/mediadrm/plugins/clearkey/default/include/DrmFactory.h diff --git a/drm/mediadrm/plugins/clearkey/DrmPlugin.h b/drm/mediadrm/plugins/clearkey/default/include/DrmPlugin.h index f37a70699a..4fa42e50af 100644 --- a/drm/mediadrm/plugins/clearkey/DrmPlugin.h +++ b/drm/mediadrm/plugins/clearkey/default/include/DrmPlugin.h @@ -133,30 +133,47 @@ public: return android::ERROR_DRM_CANNOT_HANDLE; } - virtual status_t getPropertyString( - const String8& name, String8& value) const; + virtual status_t getHdcpLevels(HdcpLevel *connectedLevel, + HdcpLevel *maxLevel) const { + UNUSED(connectedLevel); + UNUSED(maxLevel); + return android::ERROR_DRM_CANNOT_HANDLE; + } - virtual status_t getPropertyByteArray( - const String8& name, Vector<uint8_t>& value) const { - UNUSED(name); - UNUSED(value); + + virtual status_t getNumberOfSessions(uint32_t *currentSessions, + uint32_t *maxSessions) const { + UNUSED(currentSessions); + UNUSED(maxSessions); return android::ERROR_DRM_CANNOT_HANDLE; } - virtual status_t setPropertyString( - const String8& name, const String8& value) { - UNUSED(name); - UNUSED(value); + virtual status_t getSecurityLevel(Vector<uint8_t> const &sessionId, + SecurityLevel *level) const { + UNUSED(sessionId); + UNUSED(level); return android::ERROR_DRM_CANNOT_HANDLE; } - virtual status_t setPropertyByteArray( - const String8& name, const Vector<uint8_t>& value) { - UNUSED(name); - UNUSED(value); + virtual status_t setSecurityLevel(Vector<uint8_t> const &sessionId, + const SecurityLevel& level) { + UNUSED(sessionId); + UNUSED(level); return android::ERROR_DRM_CANNOT_HANDLE; } + virtual status_t getPropertyString( + const String8& name, String8& value) const; + + virtual status_t getPropertyByteArray( + const String8& name, Vector<uint8_t>& value) const; + + virtual status_t setPropertyString( + const String8& name, const String8& value); + + virtual status_t setPropertyByteArray( + const String8& name, const Vector<uint8_t>& value); + virtual status_t setCipherAlgorithm( const Vector<uint8_t>& sessionId, const String8& algorithm) { if (sessionId.size() == 0 || algorithm.size() == 0) { @@ -242,9 +259,13 @@ public: } private: + void initProperties(); void setPlayPolicy(); - android::KeyedVector<android::String8, android::String8> mPlayPolicy; + android::KeyedVector<String8, String8> mPlayPolicy; + android::KeyedVector<String8, String8> mStringProperties; + android::KeyedVector<String8, Vector<uint8_t>> mByteArrayProperties; + SessionLibrary* mSessionLibrary; DISALLOW_EVIL_CONSTRUCTORS(DrmPlugin); diff --git a/drm/mediadrm/plugins/clearkey/InitDataParser.h b/drm/mediadrm/plugins/clearkey/default/include/InitDataParser.h index a9707bfdbc..a9707bfdbc 100644 --- a/drm/mediadrm/plugins/clearkey/InitDataParser.h +++ b/drm/mediadrm/plugins/clearkey/default/include/InitDataParser.h diff --git a/drm/mediadrm/plugins/clearkey/JsonWebKey.h b/drm/mediadrm/plugins/clearkey/default/include/JsonWebKey.h index 6ae50eecfe..6ae50eecfe 100644 --- a/drm/mediadrm/plugins/clearkey/JsonWebKey.h +++ b/drm/mediadrm/plugins/clearkey/default/include/JsonWebKey.h diff --git a/drm/mediadrm/plugins/clearkey/Session.h b/drm/mediadrm/plugins/clearkey/default/include/Session.h index 09335069df..09335069df 100644 --- a/drm/mediadrm/plugins/clearkey/Session.h +++ b/drm/mediadrm/plugins/clearkey/default/include/Session.h diff --git a/drm/mediadrm/plugins/clearkey/SessionLibrary.h b/drm/mediadrm/plugins/clearkey/default/include/SessionLibrary.h index 6236fff47c..6236fff47c 100644 --- a/drm/mediadrm/plugins/clearkey/SessionLibrary.h +++ b/drm/mediadrm/plugins/clearkey/default/include/SessionLibrary.h diff --git a/drm/mediadrm/plugins/clearkey/tests/AesCtrDecryptorUnittest.cpp b/drm/mediadrm/plugins/clearkey/default/tests/AesCtrDecryptorUnittest.cpp index 5db8290a91..5db8290a91 100644 --- a/drm/mediadrm/plugins/clearkey/tests/AesCtrDecryptorUnittest.cpp +++ b/drm/mediadrm/plugins/clearkey/default/tests/AesCtrDecryptorUnittest.cpp diff --git a/drm/mediadrm/plugins/clearkey/tests/Android.bp b/drm/mediadrm/plugins/clearkey/default/tests/Android.bp index 0fcfc6436b..4419865705 100644 --- a/drm/mediadrm/plugins/clearkey/tests/Android.bp +++ b/drm/mediadrm/plugins/clearkey/default/tests/Android.bp @@ -21,12 +21,16 @@ cc_test { name: "ClearKeyDrmUnitTest", vendor: true, + cflags: ["-Wall", "-Werror"], + srcs: [ "AesCtrDecryptorUnittest.cpp", "InitDataParserUnittest.cpp", "JsonWebKeyUnittest.cpp", ], + static_libs: ["libclearkeycommon"], + shared_libs: [ "libcrypto", "libdrmclearkeyplugin", diff --git a/drm/mediadrm/plugins/clearkey/tests/InitDataParserUnittest.cpp b/drm/mediadrm/plugins/clearkey/default/tests/InitDataParserUnittest.cpp index 8c496566cc..8c496566cc 100644 --- a/drm/mediadrm/plugins/clearkey/tests/InitDataParserUnittest.cpp +++ b/drm/mediadrm/plugins/clearkey/default/tests/InitDataParserUnittest.cpp diff --git a/drm/mediadrm/plugins/clearkey/tests/JsonWebKeyUnittest.cpp b/drm/mediadrm/plugins/clearkey/default/tests/JsonWebKeyUnittest.cpp index d9f3ea6703..d9f3ea6703 100644 --- a/drm/mediadrm/plugins/clearkey/tests/JsonWebKeyUnittest.cpp +++ b/drm/mediadrm/plugins/clearkey/default/tests/JsonWebKeyUnittest.cpp diff --git a/drm/mediadrm/plugins/clearkey/hidl/AesCtrDecryptor.cpp b/drm/mediadrm/plugins/clearkey/hidl/AesCtrDecryptor.cpp new file mode 100644 index 0000000000..2fce07902e --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/AesCtrDecryptor.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "hidl_ClearkeyDecryptor" +#include <utils/Log.h> + +#include <openssl/aes.h> + +#include "AesCtrDecryptor.h" +#include "ClearKeyTypes.h" + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +using ::android::hardware::drm::V1_0::SubSample; +using ::android::hardware::drm::V1_0::Status; + +static const size_t kBlockBitCount = kBlockSize * 8; + +Status AesCtrDecryptor::decrypt( + const std::vector<uint8_t>& key, + const Iv iv, const uint8_t* source, + uint8_t* destination, + const std::vector<SubSample> subSamples, + size_t numSubSamples, + size_t* bytesDecryptedOut) { + uint32_t blockOffset = 0; + uint8_t previousEncryptedCounter[kBlockSize]; + memset(previousEncryptedCounter, 0, kBlockSize); + + if (key.size() != kBlockSize || (sizeof(Iv) / sizeof(uint8_t)) != kBlockSize) { + android_errorWriteLog(0x534e4554, "63982768"); + return Status::ERROR_DRM_DECRYPT; + } + + size_t offset = 0; + AES_KEY opensslKey; + AES_set_encrypt_key(key.data(), kBlockBitCount, &opensslKey); + Iv opensslIv; + memcpy(opensslIv, iv, sizeof(opensslIv)); + + for (size_t i = 0; i < numSubSamples; ++i) { + const SubSample& subSample = subSamples[i]; + + if (subSample.numBytesOfClearData > 0) { + memcpy(destination + offset, source + offset, + subSample.numBytesOfClearData); + offset += subSample.numBytesOfClearData; + } + + if (subSample.numBytesOfEncryptedData > 0) { + AES_ctr128_encrypt(source + offset, destination + offset, + subSample.numBytesOfEncryptedData, &opensslKey, + opensslIv, previousEncryptedCounter, + &blockOffset); + offset += subSample.numBytesOfEncryptedData; + } + } + + *bytesDecryptedOut = offset; + return Status::OK; +} + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android + diff --git a/drm/mediadrm/plugins/clearkey/hidl/Android.bp b/drm/mediadrm/plugins/clearkey/hidl/Android.bp new file mode 100644 index 0000000000..341d4f645d --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/Android.bp @@ -0,0 +1,68 @@ +// +// Copyright (C) 2018 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +cc_binary { + name: "android.hardware.drm@1.1-service.clearkey", + vendor: true, + + srcs: [ + "AesCtrDecryptor.cpp", + "Base64.cpp", + "Buffer.cpp", + "CreatePluginFactories.cpp", + "CryptoFactory.cpp", + "CryptoPlugin.cpp", + "DrmFactory.cpp", + "DrmPlugin.cpp", + "InitDataParser.cpp", + "JsonWebKey.cpp", + "Session.cpp", + "SessionLibrary.cpp", + "service.cpp", + ], + + relative_install_path: "hw", + + cflags: ["-Wall", "-Werror"], + init_rc: ["android.hardware.drm@1.1-service.clearkey.rc"], + + shared_libs: [ + "android.hardware.drm@1.0", + "android.hardware.drm@1.1", + "libbase", + "libbinder", + "libcrypto", + "libhidlbase", + "libhidlmemory", + "libhidltransport", + "liblog", + "libutils", + ], + + static_libs: [ + "libclearkeycommon", + "libjsmn", + ], + + local_include_dirs: ["include"], + + export_static_lib_headers: ["libjsmn"], + + sanitize: { + integer_overflow: true, + }, +} + diff --git a/drm/mediadrm/plugins/clearkey/hidl/Base64.cpp b/drm/mediadrm/plugins/clearkey/hidl/Base64.cpp new file mode 100644 index 0000000000..c2ed7513f4 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/Base64.cpp @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Base64.h" + +#include <string> + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +sp<Buffer> decodeBase64(const std::string &s) { + size_t n = s.size(); + + if ((n % 4) != 0) { + return nullptr; + } + + size_t padding = 0; + if (n >= 1 && s.c_str()[n - 1] == '=') { + padding = 1; + + if (n >= 2 && s.c_str()[n - 2] == '=') { + padding = 2; + + if (n >= 3 && s.c_str()[n - 3] == '=') { + padding = 3; + } + } + } + + // We divide first to avoid overflow. It's OK to do this because we + // already made sure that n % 4 == 0. + size_t outLen = (n / 4) * 3 - padding; + + sp<Buffer> buffer = new Buffer(outLen); + uint8_t *out = buffer->data(); + if (out == nullptr || buffer->size() < outLen) { + return nullptr; + } + + size_t j = 0; + uint32_t accum = 0; + for (size_t i = 0; i < n; ++i) { + char c = s.c_str()[i]; + unsigned value; + if (c >= 'A' && c <= 'Z') { + value = c - 'A'; + } else if (c >= 'a' && c <= 'z') { + value = 26 + c - 'a'; + } else if (c >= '0' && c <= '9') { + value = 52 + c - '0'; + } else if (c == '+' || c == '-') { + value = 62; + } else if (c == '/' || c == '_') { + value = 63; + } else if (c != '=') { + return nullptr; + } else { + if (i < n - padding) { + return nullptr; + } + + value = 0; + } + + accum = (accum << 6) | value; + + if (((i + 1) % 4) == 0) { + if (j < outLen) { out[j++] = (accum >> 16); } + if (j < outLen) { out[j++] = (accum >> 8) & 0xff; } + if (j < outLen) { out[j++] = accum & 0xff; } + + accum = 0; + } + } + + return buffer; +} + +static char encode6Bit(unsigned x) { + if (x <= 25) { + return 'A' + x; + } else if (x <= 51) { + return 'a' + x - 26; + } else if (x <= 61) { + return '0' + x - 52; + } else if (x == 62) { + return '+'; + } else { + return '/'; + } +} + +void encodeBase64(const void *_data, size_t size, std::string *out) { + out->clear(); + + const uint8_t *data = (const uint8_t *)_data; + + size_t i; + for (i = 0; i < (size / 3) * 3; i += 3) { + uint8_t x1 = data[i]; + uint8_t x2 = data[i + 1]; + uint8_t x3 = data[i + 2]; + + out->push_back(encode6Bit(x1 >> 2)); + out->push_back(encode6Bit((x1 << 4 | x2 >> 4) & 0x3f)); + out->push_back(encode6Bit((x2 << 2 | x3 >> 6) & 0x3f)); + out->push_back(encode6Bit(x3 & 0x3f)); + } + switch (size % 3) { + case 0: + break; + case 2: + { + uint8_t x1 = data[i]; + uint8_t x2 = data[i + 1]; + out->push_back(encode6Bit(x1 >> 2)); + out->push_back(encode6Bit((x1 << 4 | x2 >> 4) & 0x3f)); + out->push_back(encode6Bit((x2 << 2) & 0x3f)); + out->push_back('='); + break; + } + default: + { + uint8_t x1 = data[i]; + out->push_back(encode6Bit(x1 >> 2)); + out->push_back(encode6Bit((x1 << 4) & 0x3f)); + out->append("=="); + break; + } + } +} + +void encodeBase64Url(const void *_data, size_t size, std::string *out) { + encodeBase64(_data, size, out); + + if ((std::string::npos != out->find("+")) || + (std::string::npos != out->find("/"))) { + size_t outLen = out->size(); + char *base64url = new char[outLen]; + for (size_t i = 0; i < outLen; ++i) { + if (out->c_str()[i] == '+') + base64url[i] = '-'; + else if (out->c_str()[i] == '/') + base64url[i] = '_'; + else + base64url[i] = out->c_str()[i]; + } + + out->assign(base64url, outLen); + delete[] base64url; + } +} + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/Buffer.cpp b/drm/mediadrm/plugins/clearkey/hidl/Buffer.cpp new file mode 100644 index 0000000000..e58f58a379 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/Buffer.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Buffer.h" + +#include <android/hardware/drm/1.0/types.h> + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +Buffer::Buffer(size_t capacity) + : mRangeOffset(0), + mOwnsData(true) { + mData = malloc(capacity); + if (mData == nullptr) { + mCapacity = 0; + mRangeLength = 0; + } else { + mCapacity = capacity; + mRangeLength = capacity; + } +} + +Buffer::~Buffer() { + if (mOwnsData) { + if (mData != nullptr) { + free(mData); + mData = nullptr; + } + } +} + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/CreatePluginFactories.cpp b/drm/mediadrm/plugins/clearkey/hidl/CreatePluginFactories.cpp new file mode 100644 index 0000000000..1ba5c6a9ec --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/CreatePluginFactories.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CreatePluginFactories.h" + +#include "CryptoFactory.h" +#include "DrmFactory.h" + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +extern "C" { + +IDrmFactory* createDrmFactory() { + return new DrmFactory(); +} + +ICryptoFactory* createCryptoFactory() { + return new CryptoFactory(); +} + +} // extern "C" + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/CryptoFactory.cpp b/drm/mediadrm/plugins/clearkey/hidl/CryptoFactory.cpp new file mode 100644 index 0000000000..0848cef1f2 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/CryptoFactory.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "hidl_ClearKeyCryptoFactory" +#include <utils/Log.h> + +#include "CryptoFactory.h" + +#include "ClearKeyUUID.h" +#include "CryptoPlugin.h" +#include "TypeConvert.h" + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +Return<bool> CryptoFactory::isCryptoSchemeSupported( + const hidl_array<uint8_t, 16> &uuid) +{ + return clearkeydrm::isClearKeyUUID(uuid.data()); +} + +Return<void> CryptoFactory::createPlugin( + const hidl_array<uint8_t, 16> &uuid, + const hidl_vec<uint8_t> &initData, + createPlugin_cb _hidl_cb) { + + if (!isCryptoSchemeSupported(uuid.data())) { + ALOGE("Clearkey Drm HAL: failed to create clearkey plugin, " \ + "invalid crypto scheme"); + _hidl_cb(Status::BAD_VALUE, nullptr); + return Void(); + } + + CryptoPlugin *cryptoPlugin = new CryptoPlugin(initData); + Status status = cryptoPlugin->getInitStatus(); + if (status == Status::OK) { + _hidl_cb(Status::OK, cryptoPlugin); + } else { + delete cryptoPlugin; + _hidl_cb(status, nullptr); + } + return Void(); +} + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android + diff --git a/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp b/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp new file mode 100644 index 0000000000..f33f94e711 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/CryptoPlugin.cpp @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "hidl_ClearKeyCryptoPlugin" +#include <utils/Log.h> + +#include "CryptoPlugin.h" +#include "SessionLibrary.h" +#include "TypeConvert.h" + +#include <hidlmemory/mapping.h> + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +using ::android::hardware::drm::V1_0::BufferType; + +Return<void> CryptoPlugin::setSharedBufferBase( + const hidl_memory& base, uint32_t bufferId) { + sp<IMemory> hidlMemory = mapMemory(base); + ALOGE_IF(hidlMemory == nullptr, "mapMemory returns nullptr"); + + // allow mapMemory to return nullptr + mSharedBufferMap[bufferId] = hidlMemory; + return Void(); +} + +// Returns negative values for error code and positive values for the size of +// decrypted data. In theory, the output size can be larger than the input +// size, but in practice this will never happen for AES-CTR. +Return<void> CryptoPlugin::decrypt( + bool secure, + const hidl_array<uint8_t, KEY_ID_SIZE>& keyId, + const hidl_array<uint8_t, KEY_IV_SIZE>& iv, + Mode mode, + const Pattern& pattern, + const hidl_vec<SubSample>& subSamples, + const SharedBuffer& source, + uint64_t offset, + const DestinationBuffer& destination, + decrypt_cb _hidl_cb) { + UNUSED(pattern); + + if (secure) { + _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, 0, + "Secure decryption is not supported with ClearKey."); + return Void(); + } + + if (mSharedBufferMap.find(source.bufferId) == mSharedBufferMap.end()) { + _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, 0, + "source decrypt buffer base not set"); + return Void(); + } + + if (destination.type == BufferType::SHARED_MEMORY) { + const SharedBuffer& dest = destination.nonsecureMemory; + if (mSharedBufferMap.find(dest.bufferId) == mSharedBufferMap.end()) { + _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, 0, + "destination decrypt buffer base not set"); + return Void(); + } + } + + sp<IMemory> sourceBase = mSharedBufferMap[source.bufferId]; + if (sourceBase == nullptr) { + _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, 0, "source is a nullptr"); + return Void(); + } + + if (source.offset + offset + source.size > sourceBase->getSize()) { + _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, 0, "invalid buffer size"); + return Void(); + } + + uint8_t *base = static_cast<uint8_t *> + (static_cast<void *>(sourceBase->getPointer())); + uint8_t* srcPtr = static_cast<uint8_t *>(base + source.offset + offset); + void* destPtr = NULL; + if (destination.type == BufferType::SHARED_MEMORY) { + const SharedBuffer& destBuffer = destination.nonsecureMemory; + sp<IMemory> destBase = mSharedBufferMap[destBuffer.bufferId]; + if (destBase == nullptr) { + _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, 0, "destination is a nullptr"); + return Void(); + } + + if (destBuffer.offset + destBuffer.size > destBase->getSize()) { + _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, 0, "invalid buffer size"); + return Void(); + } + destPtr = static_cast<void *>(base + destination.nonsecureMemory.offset); + } else if (destination.type == BufferType::NATIVE_HANDLE) { + native_handle_t *handle = const_cast<native_handle_t *>( + destination.secureMemory.getNativeHandle()); + destPtr = static_cast<void *>(handle); + } + + // Calculate the output buffer size and determine if any subsamples are + // encrypted. + size_t destSize = 0; + bool haveEncryptedSubsamples = false; + for (size_t i = 0; i < subSamples.size(); i++) { + const SubSample &subSample = subSamples[i]; + destSize += subSample.numBytesOfClearData; + destSize += subSample.numBytesOfEncryptedData; + if (subSample.numBytesOfEncryptedData > 0) { + haveEncryptedSubsamples = true; + } + } + + if (mode == Mode::UNENCRYPTED) { + if (haveEncryptedSubsamples) { + _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, 0, + "Encrypted subsamples found in allegedly unencrypted data."); + return Void(); + } + + size_t offset = 0; + for (size_t i = 0; i < subSamples.size(); ++i) { + const SubSample& subSample = subSamples[i]; + if (subSample.numBytesOfClearData != 0) { + memcpy(reinterpret_cast<uint8_t*>(destPtr) + offset, + reinterpret_cast<const uint8_t*>(srcPtr) + offset, + subSample.numBytesOfClearData); + offset += subSample.numBytesOfClearData; + } + } + + _hidl_cb(Status::OK, static_cast<ssize_t>(offset), ""); + return Void(); + } else if (mode == Mode::AES_CTR) { + size_t bytesDecrypted; + Status res = mSession->decrypt(keyId.data(), iv.data(), srcPtr, + static_cast<uint8_t*>(destPtr), toVector(subSamples), &bytesDecrypted); + if (res == Status::OK) { + _hidl_cb(Status::OK, static_cast<ssize_t>(bytesDecrypted), ""); + return Void(); + } else { + _hidl_cb(Status::ERROR_DRM_DECRYPT, static_cast<ssize_t>(res), + "Decryption Error"); + return Void(); + } + } else { + _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, 0, + "Selected encryption mode is not supported by the ClearKey DRM Plugin."); + return Void(); + } +} + +Return<Status> CryptoPlugin::setMediaDrmSession( + const hidl_vec<uint8_t>& sessionId) { + if (!sessionId.size()) { + mSession = nullptr; + } else { + mSession = SessionLibrary::get()->findSession(sessionId); + if (!mSession.get()) { + return Status::ERROR_DRM_SESSION_NOT_OPENED; + } + } + return Status::OK; +} + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/DrmFactory.cpp b/drm/mediadrm/plugins/clearkey/hidl/DrmFactory.cpp new file mode 100644 index 0000000000..77557f9da9 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/DrmFactory.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "hidl_ClearKeyDrmFactory" +#include <utils/Log.h> + +#include <utils/Errors.h> + +#include "DrmFactory.h" + +#include "DrmPlugin.h" +#include "ClearKeyUUID.h" +#include "MimeType.h" +#include "SessionLibrary.h" + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +using ::android::hardware::drm::V1_0::Status; +using ::android::hardware::Void; + +Return<bool> DrmFactory::isCryptoSchemeSupported( + const hidl_array<uint8_t, 16>& uuid) { + return clearkeydrm::isClearKeyUUID(uuid.data()); +} + +Return<bool> DrmFactory::isContentTypeSupported(const hidl_string &mimeType) { + // This should match the mimeTypes handed by InitDataParser. + return mimeType == kIsoBmffVideoMimeType || + mimeType == kIsoBmffAudioMimeType || + mimeType == kCencInitDataFormat || + mimeType == kWebmVideoMimeType || + mimeType == kWebmAudioMimeType || + mimeType == kWebmInitDataFormat; +} + +Return<void> DrmFactory::createPlugin( + const hidl_array<uint8_t, 16>& uuid, + const hidl_string& appPackageName, + createPlugin_cb _hidl_cb) { + UNUSED(appPackageName); + + DrmPlugin *plugin = NULL; + if (!isCryptoSchemeSupported(uuid.data())) { + ALOGE("Clear key Drm HAL: failed to create drm plugin, " \ + "invalid crypto scheme"); + _hidl_cb(Status::BAD_VALUE, plugin); + return Void(); + } + + plugin = new DrmPlugin(SessionLibrary::get()); + _hidl_cb(Status::OK, plugin); + return Void(); +} + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp new file mode 100644 index 0000000000..d51e29dc57 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp @@ -0,0 +1,573 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "hidl_ClearKeyPlugin" +#include <utils/Log.h> + +#include <stdio.h> +#include <inttypes.h> + +#include "DrmPlugin.h" +#include "ClearKeyDrmProperties.h" +#include "Session.h" +#include "TypeConvert.h" + +namespace { +const int kSecureStopIdStart = 100; +const std::string kStreaming("Streaming"); +const std::string kOffline("Offline"); +const std::string kTrue("True"); + +const std::string kQueryKeyLicenseType("LicenseType"); + // Value: "Streaming" or "Offline" +const std::string kQueryKeyPlayAllowed("PlayAllowed"); + // Value: "True" or "False" +const std::string kQueryKeyRenewAllowed("RenewAllowed"); + // Value: "True" or "False" + +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 android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +DrmPlugin::DrmPlugin(SessionLibrary* sessionLibrary) + : mSessionLibrary(sessionLibrary), + mOpenSessionOkCount(0), + mCloseSessionOkCount(0), + mCloseSessionNotOpenedCount(0), + mNextSecureStopId(kSecureStopIdStart) { + mPlayPolicy.clear(); + initProperties(); + mSecureStops.clear(); +} + +void DrmPlugin::initProperties() { + mStringProperties.clear(); + mStringProperties[kVendorKey] = kVendorValue; + mStringProperties[kVersionKey] = kVersionValue; + mStringProperties[kPluginDescriptionKey] = kPluginDescriptionValue; + mStringProperties[kAlgorithmsKey] = kAlgorithmsValue; + mStringProperties[kListenerTestSupportKey] = kListenerTestSupportValue; + + std::vector<uint8_t> valueVector; + valueVector.clear(); + valueVector.insert(valueVector.end(), + kTestDeviceIdData, kTestDeviceIdData + sizeof(kTestDeviceIdData) / sizeof(uint8_t)); + mByteArrayProperties[kDeviceIdKey] = valueVector; + + valueVector.clear(); + valueVector.insert(valueVector.end(), + kMetricsData, kMetricsData + sizeof(kMetricsData) / sizeof(uint8_t)); + mByteArrayProperties[kMetricsKey] = valueVector; +} + +// 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 hidl_vec<uint8_t>& sessionId) { + 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)); +} + +Return<void> DrmPlugin::openSession(openSession_cb _hidl_cb) { + sp<Session> session = mSessionLibrary->createSession(); + std::vector<uint8_t> sessionId = session->sessionId(); + + Status status = setSecurityLevel(sessionId, SecurityLevel::SW_SECURE_CRYPTO); + _hidl_cb(status, toHidlVec(sessionId)); + mOpenSessionOkCount++; + return Void(); +} + +Return<void> DrmPlugin::openSession_1_1(SecurityLevel securityLevel, + openSession_1_1_cb _hidl_cb) { + sp<Session> session = mSessionLibrary->createSession(); + std::vector<uint8_t> sessionId = session->sessionId(); + + Status status = setSecurityLevel(sessionId, securityLevel); + _hidl_cb(status, toHidlVec(sessionId)); + mOpenSessionOkCount++; + return Void(); +} + +Return<Status> DrmPlugin::closeSession(const hidl_vec<uint8_t>& sessionId) { + if (sessionId.size() == 0) { + return Status::BAD_VALUE; + } + + sp<Session> session = mSessionLibrary->findSession(toVector(sessionId)); + if (session.get()) { + mCloseSessionOkCount++; + mSessionLibrary->destroySession(session); + return Status::OK; + } + mCloseSessionNotOpenedCount++; + return Status::ERROR_DRM_SESSION_NOT_OPENED; +} + +Status DrmPlugin::getKeyRequestCommon(const hidl_vec<uint8_t>& scope, + const hidl_vec<uint8_t>& initData, + const hidl_string& mimeType, + KeyType keyType, + const hidl_vec<KeyValue>& optionalParameters, + std::vector<uint8_t> *request, + KeyRequestType *keyRequestType, + std::string *defaultUrl) { + UNUSED(optionalParameters); + + *defaultUrl = ""; + *keyRequestType = KeyRequestType::UNKNOWN; + *request = std::vector<uint8_t>(); + + if (scope.size() == 0) { + return Status::BAD_VALUE; + } + + if (keyType != KeyType::STREAMING) { + return Status::ERROR_DRM_CANNOT_HANDLE; + } + + sp<Session> session = mSessionLibrary->findSession(toVector(scope)); + if (!session.get()) { + return Status::ERROR_DRM_SESSION_NOT_OPENED; + } + + Status status = session->getKeyRequest(initData, mimeType, request); + *keyRequestType = KeyRequestType::INITIAL; + return status; +} + +Return<void> DrmPlugin::getKeyRequest( + const hidl_vec<uint8_t>& scope, + const hidl_vec<uint8_t>& initData, + const hidl_string& mimeType, + KeyType keyType, + const hidl_vec<KeyValue>& optionalParameters, + getKeyRequest_cb _hidl_cb) { + UNUSED(optionalParameters); + + KeyRequestType keyRequestType = KeyRequestType::UNKNOWN; + std::string defaultUrl(""); + std::vector<uint8_t> request; + Status status = getKeyRequestCommon( + scope, initData, mimeType, keyType, optionalParameters, + &request, &keyRequestType, &defaultUrl); + + _hidl_cb(status, toHidlVec(request), + static_cast<drm::V1_0::KeyRequestType>(keyRequestType), + hidl_string(defaultUrl)); + return Void(); +} + +Return<void> DrmPlugin::getKeyRequest_1_1( + const hidl_vec<uint8_t>& scope, + const hidl_vec<uint8_t>& initData, + const hidl_string& mimeType, + KeyType keyType, + const hidl_vec<KeyValue>& optionalParameters, + getKeyRequest_1_1_cb _hidl_cb) { + UNUSED(optionalParameters); + + KeyRequestType keyRequestType = KeyRequestType::UNKNOWN; + std::string defaultUrl(""); + std::vector<uint8_t> request; + Status status = getKeyRequestCommon( + scope, initData, mimeType, keyType, optionalParameters, + &request, &keyRequestType, &defaultUrl); + + _hidl_cb(status, toHidlVec(request), keyRequestType, hidl_string(defaultUrl)); + return Void(); +} + +void DrmPlugin::setPlayPolicy() { + mPlayPolicy.clear(); + + KeyValue policy; + policy.key = kQueryKeyLicenseType; + policy.value = kStreaming; + mPlayPolicy.push_back(policy); + + policy.key = kQueryKeyPlayAllowed; + policy.value = kTrue; + mPlayPolicy.push_back(policy); + + policy.key = kQueryKeyRenewAllowed; + mPlayPolicy.push_back(policy); +} + +Return<void> DrmPlugin::provideKeyResponse( + const hidl_vec<uint8_t>& scope, + const hidl_vec<uint8_t>& response, + provideKeyResponse_cb _hidl_cb) { + if (scope.size() == 0 || response.size() == 0) { + // Returns empty keySetId + _hidl_cb(Status::BAD_VALUE, hidl_vec<uint8_t>()); + return Void(); + } + + sp<Session> session = mSessionLibrary->findSession(toVector(scope)); + if (!session.get()) { + _hidl_cb(Status::ERROR_DRM_SESSION_NOT_OPENED, hidl_vec<uint8_t>()); + return Void(); + } + + setPlayPolicy(); + std::vector<uint8_t> keySetId; + Status status = session->provideKeyResponse(response); + if (status == Status::OK) { + // This is for testing AMediaDrm_setOnEventListener only. + sendEvent(EventType::VENDOR_DEFINED, 0, scope); + keySetId.clear(); + } + + installSecureStop(scope); + + // Returns status and empty keySetId + _hidl_cb(status, toHidlVec(keySetId)); + return Void(); +} + +Return<void> DrmPlugin::getPropertyString( + const hidl_string& propertyName, getPropertyString_cb _hidl_cb) { + std::string name(propertyName.c_str()); + std::string value; + + if (name == 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 { + ALOGE("App requested unknown string property %s", name.c_str()); + _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, ""); + return Void(); + } + _hidl_cb(Status::OK, value.c_str()); + return Void(); +} + +Return<void> DrmPlugin::getPropertyByteArray( + const hidl_string& propertyName, getPropertyByteArray_cb _hidl_cb) { + std::map<std::string, std::vector<uint8_t> >::iterator itr = + mByteArrayProperties.find(std::string(propertyName.c_str())); + if (itr == mByteArrayProperties.end()) { + ALOGE("App requested unknown property: %s", propertyName.c_str()); + _hidl_cb(Status::BAD_VALUE, std::vector<uint8_t>()); + return Void(); + } + _hidl_cb(Status::OK, itr->second); + return Void(); + +} + +Return<Status> DrmPlugin::setPropertyString( + const hidl_string& name, const hidl_string& value) { + std::string immutableKeys; + immutableKeys.append(kAlgorithmsKey + ","); + immutableKeys.append(kPluginDescriptionKey + ","); + immutableKeys.append(kVendorKey + ","); + immutableKeys.append(kVersionKey + ","); + + std::string key = std::string(name.c_str()); + if (immutableKeys.find(key) != std::string::npos) { + ALOGD("Cannot set immutable property: %s", key.c_str()); + return Status::BAD_VALUE; + } + + std::map<std::string, std::string>::iterator itr = + mStringProperties.find(key); + if (itr == mStringProperties.end()) { + ALOGE("Cannot set undefined property string, key=%s", key.c_str()); + return Status::BAD_VALUE; + } + + mStringProperties[key] = std::string(value.c_str()); + return Status::OK; +} + +Return<Status> DrmPlugin::setPropertyByteArray( + const hidl_string& name, const hidl_vec<uint8_t>& value) { + UNUSED(value); + if (name == kDeviceIdKey) { + ALOGD("Cannot set immutable property: %s", name.c_str()); + return Status::BAD_VALUE; + } + + // Setting of undefined properties is not supported + ALOGE("Failed to set property byte array, key=%s", name.c_str()); + return Status::ERROR_DRM_CANNOT_HANDLE; +} + +Return<void> DrmPlugin::queryKeyStatus( + const hidl_vec<uint8_t>& sessionId, + queryKeyStatus_cb _hidl_cb) { + + if (sessionId.size() == 0) { + // Returns empty key status KeyValue pair + _hidl_cb(Status::BAD_VALUE, hidl_vec<KeyValue>()); + return Void(); + } + + std::vector<KeyValue> infoMapVec; + infoMapVec.clear(); + + KeyValue keyValuePair; + for (size_t i = 0; i < mPlayPolicy.size(); ++i) { + keyValuePair.key = mPlayPolicy[i].key; + keyValuePair.value = mPlayPolicy[i].value; + infoMapVec.push_back(keyValuePair); + } + _hidl_cb(Status::OK, toHidlVec(infoMapVec)); + return Void(); +} + +Return<void> DrmPlugin::getNumberOfSessions(getNumberOfSessions_cb _hidl_cb) { + uint32_t currentSessions = mSessionLibrary->numOpenSessions(); + uint32_t maxSessions = 10; + _hidl_cb(Status::OK, currentSessions, maxSessions); + return Void(); +} + +Return<void> DrmPlugin::getSecurityLevel(const hidl_vec<uint8_t>& sessionId, + getSecurityLevel_cb _hidl_cb) { + if (sessionId.size() == 0) { + _hidl_cb(Status::BAD_VALUE, SecurityLevel::UNKNOWN); + return Void(); + } + + std::vector<uint8_t> sid = toVector(sessionId); + sp<Session> session = mSessionLibrary->findSession(sid); + if (!session.get()) { + _hidl_cb(Status::ERROR_DRM_SESSION_NOT_OPENED, SecurityLevel::UNKNOWN); + return Void(); + } + + std::map<std::vector<uint8_t>, SecurityLevel>::iterator itr = + mSecurityLevel.find(sid); + if (itr == mSecurityLevel.end()) { + ALOGE("Session id not found"); + _hidl_cb(Status::ERROR_DRM_INVALID_STATE, SecurityLevel::UNKNOWN); + return Void(); + } + + _hidl_cb(Status::OK, itr->second); + return Void(); +} + +Return<Status> DrmPlugin::setSecurityLevel(const hidl_vec<uint8_t>& sessionId, + SecurityLevel level) { + if (sessionId.size() == 0) { + ALOGE("Invalid empty session id"); + return Status::BAD_VALUE; + } + + if (level > SecurityLevel::SW_SECURE_CRYPTO) { + ALOGE("Cannot set security level > max"); + return Status::ERROR_DRM_CANNOT_HANDLE; + } + + std::vector<uint8_t> sid = toVector(sessionId); + sp<Session> session = mSessionLibrary->findSession(sid); + if (!session.get()) { + return Status::ERROR_DRM_SESSION_NOT_OPENED; + } + + std::map<std::vector<uint8_t>, SecurityLevel>::iterator itr = + mSecurityLevel.find(sid); + if (itr != mSecurityLevel.end()) { + mSecurityLevel[sid] = level; + } else { + if (!mSecurityLevel.insert( + std::pair<std::vector<uint8_t>, SecurityLevel>(sid, level)).second) { + ALOGE("Failed to set security level"); + return Status::ERROR_DRM_INVALID_STATE; + } + } + return Status::OK; +} + +Return<void> DrmPlugin::getMetrics(getMetrics_cb _hidl_cb) { + // Set the open session count metric. + DrmMetricGroup::Attribute openSessionOkAttribute = { + "status", DrmMetricGroup::ValueType::INT64_TYPE, (int64_t) Status::OK, 0.0, "" + }; + DrmMetricGroup::Value openSessionMetricValue = { + "count", DrmMetricGroup::ValueType::INT64_TYPE, mOpenSessionOkCount, 0.0, "" + }; + DrmMetricGroup::Metric openSessionMetric = { + "open_session", { openSessionOkAttribute }, { openSessionMetricValue } + }; + + // Set the close session count metric. + DrmMetricGroup::Attribute closeSessionOkAttribute = { + "status", DrmMetricGroup::ValueType::INT64_TYPE, (int64_t) Status::OK, 0.0, "" + }; + DrmMetricGroup::Value closeSessionMetricValue = { + "count", DrmMetricGroup::ValueType::INT64_TYPE, mCloseSessionOkCount, 0.0, "" + }; + DrmMetricGroup::Metric closeSessionMetric = { + "close_session", { closeSessionOkAttribute }, { closeSessionMetricValue } + }; + + // Set the close session, not opened metric. + DrmMetricGroup::Attribute closeSessionNotOpenedAttribute = { + "status", DrmMetricGroup::ValueType::INT64_TYPE, + (int64_t) Status::ERROR_DRM_SESSION_NOT_OPENED, 0.0, "" + }; + DrmMetricGroup::Value closeSessionNotOpenedMetricValue = { + "count", DrmMetricGroup::ValueType::INT64_TYPE, mCloseSessionNotOpenedCount, 0.0, "" + }; + DrmMetricGroup::Metric closeSessionNotOpenedMetric = { + "close_session", { closeSessionNotOpenedAttribute }, { closeSessionNotOpenedMetricValue } + }; + + DrmMetricGroup metrics = { { openSessionMetric, closeSessionMetric, + closeSessionNotOpenedMetric } }; + + _hidl_cb(Status::OK, hidl_vec<DrmMetricGroup>({metrics})); + return Void(); +} + +Return<void> DrmPlugin::getSecureStops(getSecureStops_cb _hidl_cb) { + std::vector<SecureStop> stops; + for (auto itr = mSecureStops.begin(); itr != mSecureStops.end(); ++itr) { + ClearkeySecureStop clearkeyStop = itr->second; + std::vector<uint8_t> stopVec; + stopVec.insert(stopVec.end(), clearkeyStop.id.begin(), clearkeyStop.id.end()); + stopVec.insert(stopVec.end(), clearkeyStop.data.begin(), clearkeyStop.data.end()); + + SecureStop stop; + stop.opaqueData = toHidlVec(stopVec); + stops.push_back(stop); + } + _hidl_cb(Status::OK, stops); + return Void(); +} + +Return<void> DrmPlugin::getSecureStop(const hidl_vec<uint8_t>& secureStopId, + getSecureStop_cb _hidl_cb) { + SecureStop stop; + auto itr = mSecureStops.find(toVector(secureStopId)); + if (itr != mSecureStops.end()) { + ClearkeySecureStop clearkeyStop = itr->second; + std::vector<uint8_t> stopVec; + stopVec.insert(stopVec.end(), clearkeyStop.id.begin(), clearkeyStop.id.end()); + stopVec.insert(stopVec.end(), clearkeyStop.data.begin(), clearkeyStop.data.end()); + + stop.opaqueData = toHidlVec(stopVec); + _hidl_cb(Status::OK, stop); + } else { + _hidl_cb(Status::BAD_VALUE, stop); + } + + return Void(); +} + +Return<Status> DrmPlugin::releaseSecureStop(const hidl_vec<uint8_t>& secureStopId) { + return removeSecureStop(secureStopId); +} + +Return<Status> DrmPlugin::releaseAllSecureStops() { + return removeAllSecureStops(); +} + +Return<void> DrmPlugin::getSecureStopIds(getSecureStopIds_cb _hidl_cb) { + std::vector<SecureStopId> ids; + for (auto itr = mSecureStops.begin(); itr != mSecureStops.end(); ++itr) { + ids.push_back(itr->first); + } + + _hidl_cb(Status::OK, toHidlVec(ids)); + return Void(); +} + +Return<Status> DrmPlugin::releaseSecureStops(const SecureStopRelease& ssRelease) { + if (ssRelease.opaqueData.size() == 0) { + return Status::BAD_VALUE; + } + + Status status = Status::OK; + std::vector<uint8_t> input = toVector(ssRelease.opaqueData); + + // 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); + uint32_t count = 0; + sscanf(reinterpret_cast<char*>(input.data()), "%04" PRIu32, &count); + + // Avoid divide by 0 below. + if (count == 0) { + return Status::BAD_VALUE; + } + + size_t secureStopSize = (input.size() - countBufferSize) / count; + uint8_t buffer[secureStopSize]; + size_t offset = countBufferSize; // skip the count + for (size_t i = 0; i < count; ++i, offset += secureStopSize) { + memcpy(buffer, input.data() + offset, secureStopSize); + std::vector<uint8_t> id(buffer, buffer + kSecureStopIdSize); + + status = removeSecureStop(toHidlVec(id)); + if (Status::OK != status) break; + } + + return status; +} + +Return<Status> DrmPlugin::removeSecureStop(const hidl_vec<uint8_t>& secureStopId) { + if (1 != mSecureStops.erase(toVector(secureStopId))) { + return Status::BAD_VALUE; + } + return Status::OK; +} + +Return<Status> DrmPlugin::removeAllSecureStops() { + mSecureStops.clear(); + mNextSecureStopId = kSecureStopIdStart; + return Status::OK; +} + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/InitDataParser.cpp b/drm/mediadrm/plugins/clearkey/hidl/InitDataParser.cpp new file mode 100644 index 0000000000..e2bb6517ea --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/InitDataParser.cpp @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "hidl_InitDataParser" + +#include <algorithm> +#include <utils/Log.h> + +#include "InitDataParser.h" + +#include "Base64.h" + +#include "ClearKeyUUID.h" +#include "MimeType.h" +#include "Utils.h" + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +namespace { + const size_t kKeyIdSize = 16; + const size_t kSystemIdSize = 16; +} + +std::vector<uint8_t> StrToVector(const std::string& str) { + std::vector<uint8_t> vec(str.begin(), str.end()); + return vec; +} + +Status InitDataParser::parse(const std::vector<uint8_t>& initData, + const std::string& type, + std::vector<uint8_t>* licenseRequest) { + // Build a list of the key IDs + std::vector<const uint8_t*> keyIds; + + if (type == kIsoBmffVideoMimeType || + type == kIsoBmffAudioMimeType || + type == kCencInitDataFormat) { + Status res = parsePssh(initData, &keyIds); + if (res != Status::OK) { + return res; + } + } else if (type == kWebmVideoMimeType || + type == kWebmAudioMimeType || + type == kWebmInitDataFormat) { + // WebM "init data" is just a single key ID + if (initData.size() != kKeyIdSize) { + return Status::ERROR_DRM_CANNOT_HANDLE; + } + keyIds.push_back(initData.data()); + } else { + return Status::ERROR_DRM_CANNOT_HANDLE; + } + + // Build the request + std::string requestJson = generateRequest(keyIds); + std::vector<uint8_t> requestJsonVec = StrToVector(requestJson); + + licenseRequest->clear(); + licenseRequest->insert(licenseRequest->end(), requestJsonVec.begin(), requestJsonVec.end()); + return Status::OK; +} + +Status InitDataParser::parsePssh(const std::vector<uint8_t>& initData, + std::vector<const uint8_t*>* keyIds) { + size_t readPosition = 0; + + // Validate size field + uint32_t expectedSize = initData.size(); + expectedSize = htonl(expectedSize); + if (memcmp(&initData[readPosition], &expectedSize, + sizeof(expectedSize)) != 0) { + return Status::ERROR_DRM_CANNOT_HANDLE; + } + readPosition += sizeof(expectedSize); + + // Validate PSSH box identifier + const char psshIdentifier[4] = {'p', 's', 's', 'h'}; + if (memcmp(&initData[readPosition], psshIdentifier, + sizeof(psshIdentifier)) != 0) { + return Status::ERROR_DRM_CANNOT_HANDLE; + } + readPosition += sizeof(psshIdentifier); + + // Validate EME version number + const uint8_t psshVersion1[4] = {1, 0, 0, 0}; + if (memcmp(&initData[readPosition], psshVersion1, + sizeof(psshVersion1)) != 0) { + return Status::ERROR_DRM_CANNOT_HANDLE; + } + readPosition += sizeof(psshVersion1); + + // Validate system ID + if (!clearkeydrm::isClearKeyUUID(&initData[readPosition])) { + return Status::ERROR_DRM_CANNOT_HANDLE; + } + readPosition += kSystemIdSize; + + // Read key ID count + uint32_t keyIdCount; + memcpy(&keyIdCount, &initData[readPosition], sizeof(keyIdCount)); + keyIdCount = ntohl(keyIdCount); + readPosition += sizeof(keyIdCount); + if (readPosition + ((uint64_t)keyIdCount * kKeyIdSize) != + initData.size() - sizeof(uint32_t)) { + return Status::ERROR_DRM_CANNOT_HANDLE; + } + + // Calculate the key ID offsets + for (uint32_t i = 0; i < keyIdCount; ++i) { + size_t keyIdPosition = readPosition + (i * kKeyIdSize); + keyIds->push_back(&initData[keyIdPosition]); + } + return Status::OK; +} + +std::string InitDataParser::generateRequest(const std::vector<const uint8_t*>& keyIds) { + const std::string kRequestPrefix("{\"kids\":["); + const std::string kRequestSuffix("],\"type\":\"temporary\"}"); + + std::string request(kRequestPrefix); + std::string encodedId; + for (size_t i = 0; i < keyIds.size(); ++i) { + encodedId.clear(); + encodeBase64Url(keyIds[i], kKeyIdSize, &encodedId); + if (i != 0) { + request.append(","); + } + request.push_back('\"'); + request.append(encodedId); + request.push_back('\"'); + } + request.append(kRequestSuffix); + + // Android's Base64 encoder produces padding. EME forbids padding. + const char kBase64Padding = '='; + request.erase(std::remove(request.begin(), request.end(), kBase64Padding), request.end()); + + return request; +} + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/JsonWebKey.cpp b/drm/mediadrm/plugins/clearkey/hidl/JsonWebKey.cpp new file mode 100644 index 0000000000..cccb41ecc4 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/JsonWebKey.cpp @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define LOG_TAG "hidl_JsonWebKey" + +#include <utils/Log.h> + +#include "JsonWebKey.h" + +#include "Base64.h" + +namespace { +const std::string kKeysTag("keys"); +const std::string kKeyTypeTag("kty"); +const std::string kSymmetricKeyValue("oct"); +const std::string kKeyTag("k"); +const std::string kKeyIdTag("kid"); +const std::string kBase64Padding("="); +} + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +JsonWebKey::JsonWebKey() { +} + +JsonWebKey::~JsonWebKey() { +} + +/* + * Parses a JSON Web Key Set string, initializes a KeyMap with key id:key + * pairs from the JSON Web Key Set. Both key ids and keys are base64url + * encoded. The KeyMap contains base64url decoded key id:key pairs. + * + * @return Returns false for errors, true for success. + */ +bool JsonWebKey::extractKeysFromJsonWebKeySet(const std::string& jsonWebKeySet, + KeyMap* keys) { + + keys->clear(); + + if (!parseJsonWebKeySet(jsonWebKeySet, &mJsonObjects)) { + return false; + } + + // mJsonObjects[0] contains the entire JSON Web Key Set, including + // all the base64 encoded keys. Each key is also stored separately as + // a JSON object in mJsonObjects[1..n] where n is the total + // number of keys in the set. + if (!isJsonWebKeySet(mJsonObjects[0])) { + return false; + } + + std::string encodedKey, encodedKeyId; + std::vector<uint8_t> decodedKey, decodedKeyId; + + // mJsonObjects[1] contains the first JSON Web Key in the set + for (size_t i = 1; i < mJsonObjects.size(); ++i) { + encodedKeyId.clear(); + encodedKey.clear(); + + if (!parseJsonObject(mJsonObjects[i], &mTokens)) + return false; + + if (findKey(mJsonObjects[i], &encodedKeyId, &encodedKey)) { + if (encodedKeyId.empty() || encodedKey.empty()) { + ALOGE("Must have both key id and key in the JsonWebKey set."); + continue; + } + + if (!decodeBase64String(encodedKeyId, &decodedKeyId)) { + ALOGE("Failed to decode key id(%s)", encodedKeyId.c_str()); + continue; + } + + if (!decodeBase64String(encodedKey, &decodedKey)) { + ALOGE("Failed to decode key(%s)", encodedKey.c_str()); + continue; + } + + keys->insert(std::pair<std::vector<uint8_t>, + std::vector<uint8_t> >(decodedKeyId, decodedKey)); + } + } + return true; +} + +bool JsonWebKey::decodeBase64String(const std::string& encodedText, + std::vector<uint8_t>* decodedText) { + + decodedText->clear(); + + // encodedText should not contain padding characters as per EME spec. + if (encodedText.find(kBase64Padding) != std::string::npos) { + return false; + } + + // Since decodeBase64() requires padding characters, + // add them so length of encodedText is exactly a multiple of 4. + int remainder = encodedText.length() % 4; + std::string paddedText(encodedText); + if (remainder > 0) { + for (int i = 0; i < 4 - remainder; ++i) { + paddedText.append(kBase64Padding); + } + } + + sp<Buffer> buffer = decodeBase64(paddedText); + if (buffer == nullptr) { + ALOGE("Malformed base64 encoded content found."); + return false; + } + + decodedText->insert(decodedText->end(), buffer->base(), buffer->base() + buffer->size()); + return true; +} + +bool JsonWebKey::findKey(const std::string& jsonObject, std::string* keyId, + std::string* encodedKey) { + + std::string key, value; + + // Only allow symmetric key, i.e. "kty":"oct" pair. + if (jsonObject.find(kKeyTypeTag) != std::string::npos) { + findValue(kKeyTypeTag, &value); + if (0 != value.compare(kSymmetricKeyValue)) + return false; + } + + if (jsonObject.find(kKeyIdTag) != std::string::npos) { + findValue(kKeyIdTag, keyId); + } + + if (jsonObject.find(kKeyTag) != std::string::npos) { + findValue(kKeyTag, encodedKey); + } + return true; +} + +void JsonWebKey::findValue(const std::string &key, std::string* value) { + value->clear(); + const char* valueToken; + for (std::vector<std::string>::const_iterator nextToken = mTokens.begin(); + nextToken != mTokens.end(); ++nextToken) { + if (0 == (*nextToken).compare(key)) { + if (nextToken + 1 == mTokens.end()) + break; + valueToken = (*(nextToken + 1)).c_str(); + value->assign(valueToken); + nextToken++; + break; + } + } +} + +bool JsonWebKey::isJsonWebKeySet(const std::string& jsonObject) const { + if (jsonObject.find(kKeysTag) == std::string::npos) { + ALOGE("JSON Web Key does not contain keys."); + return false; + } + return true; +} + +/* + * Parses a JSON objects string and initializes a vector of tokens. + * + * @return Returns false for errors, true for success. + */ +bool JsonWebKey::parseJsonObject(const std::string& jsonObject, + std::vector<std::string>* tokens) { + jsmn_parser parser; + + jsmn_init(&parser); + int numTokens = jsmn_parse(&parser, + jsonObject.c_str(), jsonObject.size(), nullptr, 0); + if (numTokens < 0) { + ALOGE("Parser returns error code=%d", numTokens); + return false; + } + + unsigned int jsmnTokensSize = numTokens * sizeof(jsmntok_t); + mJsmnTokens.clear(); + mJsmnTokens.resize(jsmnTokensSize); + + jsmn_init(&parser); + int status = jsmn_parse(&parser, jsonObject.c_str(), + jsonObject.size(), mJsmnTokens.data(), numTokens); + if (status < 0) { + ALOGE("Parser returns error code=%d", status); + return false; + } + + tokens->clear(); + std::string token; + const char *pjs; + for (int j = 0; j < numTokens; ++j) { + pjs = jsonObject.c_str() + mJsmnTokens[j].start; + if (mJsmnTokens[j].type == JSMN_STRING || + mJsmnTokens[j].type == JSMN_PRIMITIVE) { + token.assign(pjs, mJsmnTokens[j].end - mJsmnTokens[j].start); + tokens->push_back(token); + } + } + return true; +} + +/* + * Parses JSON Web Key Set string and initializes a vector of JSON objects. + * + * @return Returns false for errors, true for success. + */ +bool JsonWebKey::parseJsonWebKeySet(const std::string& jsonWebKeySet, + std::vector<std::string>* jsonObjects) { + if (jsonWebKeySet.empty()) { + ALOGE("Empty JSON Web Key"); + return false; + } + + // The jsmn parser only supports unicode encoding. + jsmn_parser parser; + + // Computes number of tokens. A token marks the type, offset in + // the original string. + jsmn_init(&parser); + int numTokens = jsmn_parse(&parser, + jsonWebKeySet.c_str(), jsonWebKeySet.size(), nullptr, 0); + if (numTokens < 0) { + ALOGE("Parser returns error code=%d", numTokens); + return false; + } + + unsigned int jsmnTokensSize = numTokens * sizeof(jsmntok_t); + mJsmnTokens.resize(jsmnTokensSize); + + jsmn_init(&parser); + int status = jsmn_parse(&parser, jsonWebKeySet.c_str(), + jsonWebKeySet.size(), mJsmnTokens.data(), numTokens); + if (status < 0) { + ALOGE("Parser returns error code=%d", status); + return false; + } + + std::string token; + const char *pjs; + for (int i = 0; i < numTokens; ++i) { + pjs = jsonWebKeySet.c_str() + mJsmnTokens[i].start; + if (mJsmnTokens[i].type == JSMN_OBJECT) { + token.assign(pjs, mJsmnTokens[i].end - mJsmnTokens[i].start); + jsonObjects->push_back(token); + } + } + return true; +} + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android + diff --git a/drm/mediadrm/plugins/clearkey/hidl/Session.cpp b/drm/mediadrm/plugins/clearkey/hidl/Session.cpp new file mode 100644 index 0000000000..07c9269c68 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/Session.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "hidl_ClearKeySession" +#include <utils/Log.h> + +#include "Session.h" +#include "Utils.h" + +#include "AesCtrDecryptor.h" +#include "InitDataParser.h" +#include "JsonWebKey.h" + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +using ::android::hardware::drm::V1_0::KeyValue; +using ::android::hardware::drm::V1_0::Status; +using ::android::hardware::drm::V1_0::SubSample; +using ::android::hardware::Return; +using ::android::sp; + +using android::Mutex; + +Status Session::getKeyRequest( + const std::vector<uint8_t>& initData, + const std::string& mimeType, + std::vector<uint8_t>* keyRequest) const { + InitDataParser parser; + return parser.parse(initData, mimeType, keyRequest); +} + +Status Session::provideKeyResponse(const std::vector<uint8_t>& response) { + std::string responseString( + reinterpret_cast<const char*>(response.data()), response.size()); + KeyMap keys; + + Mutex::Autolock lock(mMapLock); + JsonWebKey parser; + if (parser.extractKeysFromJsonWebKeySet(responseString, &keys)) { + for (auto &key : keys) { + std::string first(key.first.begin(), key.first.end()); + std::string second(key.second.begin(), key.second.end()); + mKeyMap.insert(std::pair<std::vector<uint8_t>, + std::vector<uint8_t> >(key.first, key.second)); + } + return Status::OK; + } else { + return Status::ERROR_DRM_UNKNOWN; + } +} + +Status Session::decrypt( + const KeyId keyId, const Iv iv, const uint8_t* srcPtr, + uint8_t* destPtr, const std::vector<SubSample> subSamples, + size_t* bytesDecryptedOut) { + Mutex::Autolock lock(mMapLock); + + std::vector<uint8_t> keyIdVector; + keyIdVector.clear(); + keyIdVector.insert(keyIdVector.end(), keyId, keyId + kBlockSize); + std::map<std::vector<uint8_t>, std::vector<uint8_t> >::iterator itr; + itr = mKeyMap.find(keyIdVector); + if (itr == mKeyMap.end()) { + return Status::ERROR_DRM_NO_LICENSE; + } + + AesCtrDecryptor decryptor; + return decryptor.decrypt( + itr->second /*key*/, iv, srcPtr, destPtr, subSamples, + subSamples.size(), bytesDecryptedOut); +} + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/SessionLibrary.cpp b/drm/mediadrm/plugins/clearkey/hidl/SessionLibrary.cpp new file mode 100644 index 0000000000..b4319e6a45 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/SessionLibrary.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "hidl_ClearKeySessionLibrary" +#include <utils/Log.h> + +#include "SessionLibrary.h" +#include "Utils.h" + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +using ::android::hardware::hidl_string; +using ::android::hardware::hidl_vec; +using ::android::sp; + +Mutex SessionLibrary::sSingletonLock; +SessionLibrary* SessionLibrary::sSingleton = NULL; + +SessionLibrary* SessionLibrary::get() { + Mutex::Autolock lock(sSingletonLock); + + if (sSingleton == NULL) { + ALOGD("Instantiating Session Library Singleton."); + sSingleton = new SessionLibrary(); + } + + return sSingleton; +} + +sp<Session> SessionLibrary::createSession() { + Mutex::Autolock lock(mSessionsLock); + + char sessionIdRaw[16]; + snprintf(sessionIdRaw, sizeof(sessionIdRaw), "%u", mNextSessionId); + + mNextSessionId += 1; + + std::vector<uint8_t> sessionId; + sessionId.insert(sessionId.end(), sessionIdRaw, + sessionIdRaw + sizeof(sessionIdRaw) / sizeof(uint8_t)); + + mSessions.insert(std::pair<std::vector<uint8_t>, + sp<Session> >(sessionId, new Session(sessionId))); + std::map<std::vector<uint8_t>, sp<Session> >::iterator itr = mSessions.find(sessionId); + if (itr != mSessions.end()) { + return itr->second; + } else { + return nullptr; + } +} + +sp<Session> SessionLibrary::findSession( + const std::vector<uint8_t>& sessionId) { + Mutex::Autolock lock(mSessionsLock); + std::map<std::vector<uint8_t>, sp<Session> >::iterator itr = mSessions.find(sessionId); + if (itr != mSessions.end()) { + return itr->second; + } else { + return nullptr; + } +} + +void SessionLibrary::destroySession(const sp<Session>& session) { + Mutex::Autolock lock(mSessionsLock); + mSessions.erase(session->sessionId()); +} + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android diff --git a/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.1-service.clearkey.rc b/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.1-service.clearkey.rc new file mode 100644 index 0000000000..ffe856a55d --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/android.hardware.drm@1.1-service.clearkey.rc @@ -0,0 +1,6 @@ +service vendor.drm-clearkey-hal-1-1 /vendor/bin/hw/android.hardware.drm@1.1-service.clearkey + class hal + user media + group media mediadrm + ioprio rt 4 + writepid /dev/cpuset/foreground/tasks diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/AesCtrDecryptor.h b/drm/mediadrm/plugins/clearkey/hidl/include/AesCtrDecryptor.h new file mode 100644 index 0000000000..0c7ef20fdf --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/include/AesCtrDecryptor.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CLEARKEY_AES_CTR_DECRYPTOR_H_ +#define CLEARKEY_AES_CTR_DECRYPTOR_H_ + +#include "ClearKeyTypes.h" + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +using ::android::hardware::drm::V1_0::Status; +using ::android::hardware::drm::V1_0::SubSample; + +class AesCtrDecryptor { +public: + AesCtrDecryptor() {} + + Status decrypt(const std::vector<uint8_t>& key, const Iv iv, + const uint8_t* source, uint8_t* destination, + const std::vector<SubSample> subSamples, size_t numSubSamples, + size_t* bytesDecryptedOut); + +private: + CLEARKEY_DISALLOW_COPY_AND_ASSIGN(AesCtrDecryptor); +}; + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android + +#endif // CLEARKEY_AES_CTR_DECRYPTOR_H_ diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/Base64.h b/drm/mediadrm/plugins/clearkey/hidl/include/Base64.h new file mode 100644 index 0000000000..4a385bdb6e --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/include/Base64.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef BASE_64_H_ + +#define BASE_64_H_ + +#include <android/hardware/drm/1.0/types.h> + +#include "Buffer.h" + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +using ::android::sp; + +struct Buffer; + +sp<Buffer> decodeBase64(const std::string &s); +void encodeBase64(const void *data, size_t size, std::string *out); + +void encodeBase64Url(const void *data, size_t size, std::string *out); + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android + +#endif // BASE_64_H_ diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/Buffer.h b/drm/mediadrm/plugins/clearkey/hidl/include/Buffer.h new file mode 100644 index 0000000000..5bbb28aa14 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/include/Buffer.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef BUFFER_H_ +#define BUFFER_H_ + +#include <android/hardware/drm/1.0/types.h> +#include <utils/RefBase.h> + +#include "ClearKeyTypes.h" + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +using ::android::sp; + +struct Buffer : public RefBase { + explicit Buffer(size_t capacity); + + uint8_t *base() { return reinterpret_cast<uint8_t *>(mData); } + uint8_t *data() { return reinterpret_cast<uint8_t *>(mData) + mRangeOffset; } + size_t capacity() const { return mCapacity; } + size_t size() const { return mRangeLength; } + size_t offset() const { return mRangeOffset; } + +protected: + virtual ~Buffer(); + +private: + void *mData; + size_t mCapacity; + size_t mRangeOffset; + size_t mRangeLength; + + bool mOwnsData; + + CLEARKEY_DISALLOW_COPY_AND_ASSIGN(Buffer); +}; + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android + +#endif // BUFFER_H_ diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyDrmProperties.h b/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyDrmProperties.h new file mode 100644 index 0000000000..d65b25c37f --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyDrmProperties.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CLEARKEY_DRM_PROPERTIES_H_ +#define CLEARKEY_DRM_PROPERTIES_H_ + +#include <string.h> + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +static const std::string kVendorKey("vendor"); +static const std::string kVendorValue("Google"); +static const std::string kVersionKey("version"); +static const std::string kVersionValue("1.1"); +static const std::string kPluginDescriptionKey("description"); +static const std::string kPluginDescriptionValue("ClearKey CDM"); +static const std::string kAlgorithmsKey("algorithms"); +static const std::string kAlgorithmsValue(""); +static const std::string kListenerTestSupportKey("listenerTestSupport"); +static const std::string kListenerTestSupportValue("true"); + +static const std::string kDeviceIdKey("deviceId"); +static const uint8_t kTestDeviceIdData[] = + {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}; +// TODO stub out metrics for nw +static const std::string kMetricsKey("metrics"); +static const uint8_t kMetricsData[] = { 0 }; + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android + +#endif // CLEARKEY_DRM_PROPERTIES_H_ + diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyTypes.h b/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyTypes.h new file mode 100644 index 0000000000..46cb5e4dd5 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/include/ClearKeyTypes.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CLEARKEY_MACROS_H_ +#define CLEARKEY_MACROS_H_ + +#include <android/hardware/drm/1.0/types.h> + +#include <map> + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +using ::android::hardware::drm::V1_0::KeyValue; +using ::android::hardware::hidl_vec; + +const uint8_t kBlockSize = 16; //AES_BLOCK_SIZE; +typedef uint8_t KeyId[kBlockSize]; +typedef uint8_t Iv[kBlockSize]; + +typedef ::android::hardware::drm::V1_0::SubSample SubSample; +typedef std::map<std::vector<uint8_t>, std::vector<uint8_t> > KeyMap; + +#define CLEARKEY_DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&) = delete; \ + void operator=(const TypeName&) = delete; + +#define CLEARKEY_DISALLOW_COPY_AND_ASSIGN_AND_NEW(TypeName) \ + TypeName() = delete; \ + TypeName(const TypeName&) = delete; \ + void operator=(const TypeName&) = delete; + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android + +#endif // CLEARKEY_MACROS_H_ diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/CreatePluginFactories.h b/drm/mediadrm/plugins/clearkey/hidl/include/CreatePluginFactories.h new file mode 100644 index 0000000000..99520277f2 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/include/CreatePluginFactories.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CLEARKEY_CREATE_PLUGIN_FACTORIES_H_ +#define CLEARKEY_CREATE_PLUGIN_FACTORIES_H_ + +#include <android/hardware/drm/1.1/ICryptoFactory.h> +#include <android/hardware/drm/1.1/IDrmFactory.h> + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +using ::android::hardware::drm::V1_1::ICryptoFactory; +using ::android::hardware::drm::V1_1::IDrmFactory; + +extern "C" { + IDrmFactory* createDrmFactory(); + ICryptoFactory* createCryptoFactory(); +} + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android +#endif // CLEARKEY_CREATE_PLUGIN_FACTORIES_H_ diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/CryptoFactory.h b/drm/mediadrm/plugins/clearkey/hidl/include/CryptoFactory.h new file mode 100644 index 0000000000..175ab761fa --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/include/CryptoFactory.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CLEARKEY_CRYPTO_FACTORY_H_ +#define CLEARKEY_CRYPTO_FACTORY_H_ + +#include <android/hardware/drm/1.0/ICryptoPlugin.h> +#include <android/hardware/drm/1.1/ICryptoFactory.h> + +#include "ClearKeyTypes.h" + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +using ::android::hardware::drm::V1_1::ICryptoFactory; +using ::android::hardware::drm::V1_0::ICryptoPlugin; +using ::android::hardware::hidl_array; +using ::android::hardware::hidl_string; +using ::android::hardware::Return; + +struct CryptoFactory : public ICryptoFactory { + CryptoFactory() {} + virtual ~CryptoFactory() {} + + Return<bool> isCryptoSchemeSupported(const hidl_array<uint8_t, 16>& uuid) + override; + + Return<void> createPlugin( + const hidl_array<uint8_t, 16>& uuid, + const hidl_vec<uint8_t>& initData, + createPlugin_cb _hidl_cb) override; + +private: + CLEARKEY_DISALLOW_COPY_AND_ASSIGN(CryptoFactory); + +}; + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android + +#endif // CLEARKEY_CRYPTO_FACTORY_H_ diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/CryptoPlugin.h b/drm/mediadrm/plugins/clearkey/hidl/include/CryptoPlugin.h new file mode 100644 index 0000000000..6a738066d0 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/include/CryptoPlugin.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CLEARKEY_CRYPTO_PLUGIN_H_ +#define CLEARKEY_CRYPTO_PLUGIN_H_ + +#include <android/hardware/drm/1.0/ICryptoPlugin.h> +#include <android/hidl/memory/1.0/IMemory.h> + +#include "ClearKeyTypes.h" +#include "Session.h" +#include "Utils.h" + +namespace { + static const size_t KEY_ID_SIZE = 16; + static const size_t KEY_IV_SIZE = 16; +} + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +using ::android::hardware::drm::V1_0::DestinationBuffer; +using ::android::hardware::drm::V1_0::ICryptoPlugin; +using ::android::hardware::drm::V1_0::Mode; +using ::android::hardware::drm::V1_0::Pattern; +using ::android::hardware::drm::V1_0::SharedBuffer; +using ::android::hardware::drm::V1_0::Status; +using ::android::hardware::drm::V1_0::SubSample; +using ::android::hardware::hidl_array; +using ::android::hardware::hidl_memory; +using ::android::hardware::hidl_string; +using ::android::hardware::hidl_vec; +using ::android::hardware::Return; +using ::android::hardware::Void; +using ::android::hidl::memory::V1_0::IMemory; +using ::android::sp; + +struct CryptoPlugin : public ICryptoPlugin { + explicit CryptoPlugin(const hidl_vec<uint8_t>& sessionId) { + mInitStatus = setMediaDrmSession(sessionId); + } + virtual ~CryptoPlugin() {} + + Return<bool> requiresSecureDecoderComponent(const hidl_string& mime) { + UNUSED(mime); + return false; + } + + Return<void> notifyResolution(uint32_t width, uint32_t height) { + UNUSED(width); + UNUSED(height); + return Void(); + } + + Return<void> decrypt( + bool secure, + const hidl_array<uint8_t, KEY_ID_SIZE>& keyId, + const hidl_array<uint8_t, KEY_IV_SIZE>& iv, + Mode mode, + const Pattern& pattern, + const hidl_vec<SubSample>& subSamples, + const SharedBuffer& source, + uint64_t offset, + const DestinationBuffer& destination, + decrypt_cb _hidl_cb); + + Return<void> setSharedBufferBase(const hidl_memory& base, + uint32_t bufferId); + + Return<Status> setMediaDrmSession(const hidl_vec<uint8_t>& sessionId); + + Return<Status> getInitStatus() const { return mInitStatus; } + +private: + CLEARKEY_DISALLOW_COPY_AND_ASSIGN(CryptoPlugin); + + std::map<uint32_t, sp<IMemory> > mSharedBufferMap; + sp<Session> mSession; + Status mInitStatus; +}; + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android + +#endif // CLEARKEY_CRYPTO_PLUGIN_H_ diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/DrmFactory.h b/drm/mediadrm/plugins/clearkey/hidl/include/DrmFactory.h new file mode 100644 index 0000000000..6f581955b5 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/include/DrmFactory.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CLEARKEY_DRM_FACTORY_H_ +#define CLEARKEY_DRM_FACTORY_H_ + +#include <android/hardware/drm/1.1/IDrmPlugin.h> +#include <android/hardware/drm/1.1/IDrmFactory.h> + +#include "ClearKeyTypes.h" + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +using ::android::hardware::hidl_array; +using ::android::hardware::hidl_string; +using ::android::hardware::Return; + +struct DrmFactory : public IDrmFactory { + DrmFactory() {} + virtual ~DrmFactory() {} + + Return<bool> isCryptoSchemeSupported(const hidl_array<uint8_t, 16>& uuid) + override; + + Return<bool> isContentTypeSupported(const hidl_string &mimeType) + override; + + Return<void> createPlugin( + const hidl_array<uint8_t, 16>& uuid, + const hidl_string& appPackageName, + createPlugin_cb _hidl_cb) override; + +private: + CLEARKEY_DISALLOW_COPY_AND_ASSIGN(DrmFactory); +}; + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android + +#endif // CLEARKEY_DRM_FACTORY_H_ diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h b/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h new file mode 100644 index 0000000000..fb0695aaef --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h @@ -0,0 +1,343 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CLEARKEY_DRM_PLUGIN_H_ +#define CLEARKEY_DRM_PLUGIN_H_ + +#include <android/hardware/drm/1.1/IDrmPlugin.h> + +#include <stdio.h> +#include <map> + +#include "SessionLibrary.h" +#include "Utils.h" + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +using ::android::hardware::drm::V1_0::EventType; +using ::android::hardware::drm::V1_0::IDrmPluginListener; +using ::android::hardware::drm::V1_0::KeyStatus; +using ::android::hardware::drm::V1_0::KeyType; +using ::android::hardware::drm::V1_0::KeyValue; +using ::android::hardware::drm::V1_0::SecureStop; +using ::android::hardware::drm::V1_0::SecureStopId; +using ::android::hardware::drm::V1_0::SessionId; +using ::android::hardware::drm::V1_0::Status; +using ::android::hardware::drm::V1_1::DrmMetricGroup; +using ::android::hardware::drm::V1_1::IDrmPlugin; +using ::android::hardware::drm::V1_1::KeyRequestType; + +using ::android::hardware::hidl_string; +using ::android::hardware::hidl_vec; +using ::android::hardware::Return; +using ::android::hardware::Void; +using ::android::sp; + +struct DrmPlugin : public IDrmPlugin { + explicit DrmPlugin(SessionLibrary* sessionLibrary); + + virtual ~DrmPlugin() {} + + Return<void> openSession(openSession_cb _hidl_cb) override; + Return<void> openSession_1_1(SecurityLevel securityLevel, + openSession_cb _hidl_cb) override; + + Return<Status> closeSession(const hidl_vec<uint8_t>& sessionId) override; + + Return<void> getKeyRequest( + const hidl_vec<uint8_t>& scope, + const hidl_vec<uint8_t>& initData, + const hidl_string& mimeType, + KeyType keyType, + const hidl_vec<KeyValue>& optionalParameters, + getKeyRequest_cb _hidl_cb) override; + + Return<void> getKeyRequest_1_1( + const hidl_vec<uint8_t>& scope, + const hidl_vec<uint8_t>& initData, + const hidl_string& mimeType, + KeyType keyType, + const hidl_vec<KeyValue>& optionalParameters, + getKeyRequest_1_1_cb _hidl_cb) override; + + Return<void> provideKeyResponse( + const hidl_vec<uint8_t>& scope, + const hidl_vec<uint8_t>& response, + provideKeyResponse_cb _hidl_cb) override; + + Return<Status> removeKeys(const hidl_vec<uint8_t>& sessionId) { + if (sessionId.size() == 0) { + return Status::BAD_VALUE; + } + return Status::ERROR_DRM_CANNOT_HANDLE; + } + + Return<Status> restoreKeys( + const hidl_vec<uint8_t>& sessionId, + const hidl_vec<uint8_t>& keySetId) { + + if (sessionId.size() == 0 || keySetId.size() == 0) { + return Status::BAD_VALUE; + } + return Status::ERROR_DRM_CANNOT_HANDLE; + } + + Return<void> queryKeyStatus( + const hidl_vec<uint8_t>& sessionId, + queryKeyStatus_cb _hidl_cb) override; + + Return<void> getProvisionRequest( + const hidl_string& certificateType, + const hidl_string& certificateAuthority, + getProvisionRequest_cb _hidl_cb) { + UNUSED(certificateType); + UNUSED(certificateAuthority); + + hidl_string defaultUrl; + _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, hidl_vec<uint8_t>(), defaultUrl); + return Void(); + } + + Return<void> provideProvisionResponse( + const hidl_vec<uint8_t>& response, + provideProvisionResponse_cb _hidl_cb) { + + if (response.size() == 0) { + _hidl_cb(Status::BAD_VALUE, hidl_vec<uint8_t>(), hidl_vec<uint8_t>()); + return Void(); + } + _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, hidl_vec<uint8_t>(), hidl_vec<uint8_t>()); + return Void(); + } + + Return<void> getHdcpLevels(getHdcpLevels_cb _hidl_cb) { + HdcpLevel connectedLevel = HdcpLevel::HDCP_NONE; + HdcpLevel maxLevel = HdcpLevel::HDCP_NO_OUTPUT; + _hidl_cb(Status::OK, connectedLevel, maxLevel); + return Void(); + } + + Return<void> getNumberOfSessions(getNumberOfSessions_cb _hidl_cb) override; + + Return<void> getSecurityLevel(const hidl_vec<uint8_t>& sessionId, + getSecurityLevel_cb _hidl_cb) override; + + Return<void> getMetrics(getMetrics_cb _hidl_cb) override; + + Return<void> getPropertyString( + const hidl_string& name, + getPropertyString_cb _hidl_cb) override; + + Return<void> getPropertyByteArray( + const hidl_string& name, + getPropertyByteArray_cb _hidl_cb) override; + + Return<Status> setPropertyString( + const hidl_string& name, const hidl_string& value) override; + + Return<Status> setPropertyByteArray( + const hidl_string& name, const hidl_vec<uint8_t>& value) override; + + Return<Status> setCipherAlgorithm( + const hidl_vec<uint8_t>& sessionId, const hidl_string& algorithm) { + if (sessionId.size() == 0 || algorithm.size() == 0) { + return Status::BAD_VALUE; + } + return Status::ERROR_DRM_CANNOT_HANDLE; + } + + Return<Status> setMacAlgorithm( + const hidl_vec<uint8_t>& sessionId, const hidl_string& algorithm) { + if (sessionId.size() == 0 || algorithm.size() == 0) { + return Status::BAD_VALUE; + } + return Status::ERROR_DRM_CANNOT_HANDLE; + } + + Return<void> encrypt( + const hidl_vec<uint8_t>& sessionId, + const hidl_vec<uint8_t>& keyId, + const hidl_vec<uint8_t>& input, + const hidl_vec<uint8_t>& iv, + encrypt_cb _hidl_cb) { + if (sessionId.size() == 0 || keyId.size() == 0 || + input.size() == 0 || iv.size() == 0) { + _hidl_cb(Status::BAD_VALUE, hidl_vec<uint8_t>()); + return Void(); + } + _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, hidl_vec<uint8_t>()); + return Void(); + } + + Return<void> decrypt( + const hidl_vec<uint8_t>& sessionId, + const hidl_vec<uint8_t>& keyId, + const hidl_vec<uint8_t>& input, + const hidl_vec<uint8_t>& iv, + decrypt_cb _hidl_cb) { + if (sessionId.size() == 0 || keyId.size() == 0 || + input.size() == 0 || iv.size() == 0) { + _hidl_cb(Status::BAD_VALUE, hidl_vec<uint8_t>()); + return Void(); + } + _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, hidl_vec<uint8_t>()); + return Void(); + } + + Return<void> sign( + const hidl_vec<uint8_t>& sessionId, + const hidl_vec<uint8_t>& keyId, + const hidl_vec<uint8_t>& message, + sign_cb _hidl_cb) { + if (sessionId.size() == 0 || keyId.size() == 0 || + message.size() == 0) { + _hidl_cb(Status::BAD_VALUE, hidl_vec<uint8_t>()); + return Void(); + } + _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, hidl_vec<uint8_t>()); + return Void(); + } + + Return<void> verify( + const hidl_vec<uint8_t>& sessionId, + const hidl_vec<uint8_t>& keyId, + const hidl_vec<uint8_t>& message, + const hidl_vec<uint8_t>& signature, + verify_cb _hidl_cb) { + + if (sessionId.size() == 0 || keyId.size() == 0 || + message.size() == 0 || signature.size() == 0) { + _hidl_cb(Status::BAD_VALUE, false); + return Void(); + } + _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, false); + return Void(); + } + + Return<void> signRSA( + const hidl_vec<uint8_t>& sessionId, + const hidl_string& algorithm, + const hidl_vec<uint8_t>& message, + const hidl_vec<uint8_t>& wrappedKey, + signRSA_cb _hidl_cb) { + if (sessionId.size() == 0 || algorithm.size() == 0 || + message.size() == 0 || wrappedKey.size() == 0) { + _hidl_cb(Status::BAD_VALUE, hidl_vec<uint8_t>()); + return Void(); + } + _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, hidl_vec<uint8_t>()); + return Void(); + } + + Return<void> setListener(const sp<IDrmPluginListener>& listener) { + mListener = listener; + return Void(); + }; + + Return<void> sendEvent(EventType eventType, const hidl_vec<uint8_t>& sessionId, + const hidl_vec<uint8_t>& data) { + if (mListener != NULL) { + mListener->sendEvent(eventType, sessionId, data); + } else { + ALOGE("Null event listener, event not sent"); + } + return Void(); + } + + Return<void> sendExpirationUpdate(const hidl_vec<uint8_t>& sessionId, int64_t expiryTimeInMS) { + if (mListener != NULL) { + mListener->sendExpirationUpdate(sessionId, expiryTimeInMS); + } else { + ALOGE("Null event listener, event not sent"); + } + return Void(); + } + + Return<void> sendKeysChange(const hidl_vec<uint8_t>& sessionId, + const hidl_vec<KeyStatus>& keyStatusList, bool hasNewUsableKey) { + if (mListener != NULL) { + mListener->sendKeysChange(sessionId, keyStatusList, hasNewUsableKey); + } else { + ALOGE("Null event listener, event not sent"); + } + return Void(); + } + + Return<void> getSecureStops(getSecureStops_cb _hidl_cb); + + Return<void> getSecureStop(const hidl_vec<uint8_t>& secureStopId, + getSecureStop_cb _hidl_cb); + + Return<Status> releaseSecureStop(const hidl_vec<uint8_t>& ssRelease); + + Return<Status> releaseAllSecureStops(); + + Return<void> getSecureStopIds(getSecureStopIds_cb _hidl_cb); + + Return<Status> releaseSecureStops(const SecureStopRelease& ssRelease); + + Return<Status> removeSecureStop(const hidl_vec<uint8_t>& secureStopId); + + Return<Status> removeAllSecureStops(); + +private: + void initProperties(); + void installSecureStop(const hidl_vec<uint8_t>& sessionId); + void setPlayPolicy(); + + Return<Status> setSecurityLevel(const hidl_vec<uint8_t>& sessionId, + SecurityLevel level); + + Status getKeyRequestCommon(const hidl_vec<uint8_t>& scope, + const hidl_vec<uint8_t>& initData, + const hidl_string& mimeType, + KeyType keyType, + const hidl_vec<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::vector<uint8_t>, SecurityLevel> mSecurityLevel; + sp<IDrmPluginListener> mListener; + SessionLibrary *mSessionLibrary; + int64_t mOpenSessionOkCount; + int64_t mCloseSessionOkCount; + int64_t mCloseSessionNotOpenedCount; + uint32_t mNextSecureStopId; + + CLEARKEY_DISALLOW_COPY_AND_ASSIGN_AND_NEW(DrmPlugin); +}; + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android + +#endif // CLEARKEY_DRM_PLUGIN_H_ diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/InitDataParser.h b/drm/mediadrm/plugins/clearkey/hidl/include/InitDataParser.h new file mode 100644 index 0000000000..3189c4aeb2 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/include/InitDataParser.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CLEARKEY_INIT_DATA_PARSER_H_ +#define CLEARKEY_INIT_DATA_PARSER_H_ + +#include <android/hardware/drm/1.0/types.h> + +#include "ClearKeyTypes.h" + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +using ::android::hardware::drm::V1_0::Status; + +class InitDataParser { +public: + InitDataParser() {} + + Status parse(const std::vector<uint8_t>& initData, + const std::string& type, + std::vector<uint8_t>* licenseRequest); + +private: + CLEARKEY_DISALLOW_COPY_AND_ASSIGN(InitDataParser); + + Status parsePssh(const std::vector<uint8_t>& initData, + std::vector<const uint8_t*>* keyIds); + + std::string generateRequest( + const std::vector<const uint8_t*>& keyIds); +}; + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android + +#endif // CLEARKEY_INIT_DATA_PARSER_H_ diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/JsonWebKey.h b/drm/mediadrm/plugins/clearkey/hidl/include/JsonWebKey.h new file mode 100644 index 0000000000..4ab034cd74 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/include/JsonWebKey.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CLEARKEY_JSON_WEB_KEY_H_ +#define CLEARKEY_JSON_WEB_KEY_H_ + +#include "jsmn.h" +#include "Utils.h" +#include "ClearKeyTypes.h" + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +class JsonWebKey { + public: + JsonWebKey(); + virtual ~JsonWebKey(); + + bool extractKeysFromJsonWebKeySet(const std::string& jsonWebKeySet, + KeyMap* keys); + + private: + std::vector<jsmntok_t> mJsmnTokens; + std::vector<std::string> mJsonObjects; + std::vector<std::string> mTokens; + + bool decodeBase64String(const std::string& encodedText, + std::vector<uint8_t>* decodedText); + bool findKey(const std::string& jsonObject, std::string* keyId, + std::string* encodedKey); + void findValue(const std::string &key, std::string* value); + bool isJsonWebKeySet(const std::string& jsonObject) const; + bool parseJsonObject(const std::string& jsonObject, + std::vector<std::string>* tokens); + bool parseJsonWebKeySet(const std::string& jsonWebKeySet, + std::vector<std::string>* jsonObjects); + + CLEARKEY_DISALLOW_COPY_AND_ASSIGN(JsonWebKey); +}; + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android + +#endif // CLEARKEY_JSON_WEB_KEY_H_ diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/Session.h b/drm/mediadrm/plugins/clearkey/hidl/include/Session.h new file mode 100644 index 0000000000..cddfca5eba --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/include/Session.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CLEARKEY_SESSION_H_ +#define CLEARKEY_SESSION_H_ + +#include <utils/Mutex.h> +#include <utils/RefBase.h> +#include <vector> + +#include "ClearKeyTypes.h" + + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +using ::android::hardware::drm::V1_0::Status; +using ::android::hardware::drm::V1_0::SubSample; + +class Session : public RefBase { +public: + explicit Session(const std::vector<uint8_t>& sessionId) + : mSessionId(sessionId) {} + virtual ~Session() {} + + const std::vector<uint8_t>& sessionId() const { return mSessionId; } + + Status getKeyRequest( + const std::vector<uint8_t>& mimeType, + const std::string& initDataType, + std::vector<uint8_t>* keyRequest) const; + + Status provideKeyResponse( + const std::vector<uint8_t>& response); + + Status decrypt( + const KeyId keyId, const Iv iv, const uint8_t* srcPtr, + uint8_t* dstPtr, const std::vector<SubSample> subSamples, + size_t* bytesDecryptedOut); + +private: + CLEARKEY_DISALLOW_COPY_AND_ASSIGN(Session); + + const std::vector<uint8_t> mSessionId; + KeyMap mKeyMap; + Mutex mMapLock; +}; + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android + +#endif // CLEARKEY_SESSION_H_ diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/SessionLibrary.h b/drm/mediadrm/plugins/clearkey/hidl/include/SessionLibrary.h new file mode 100644 index 0000000000..326a0c100d --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/include/SessionLibrary.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CLEARKEY_SESSION_LIBRARY_H_ +#define CLEARKEY_SESSION_LIBRARY_H_ + +#include <utils/RefBase.h> +#include <utils/Mutex.h> + +#include "ClearKeyTypes.h" +#include "Session.h" + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +using ::android::sp; + +class SessionLibrary : public RefBase { +public: + static SessionLibrary* get(); + + sp<Session> createSession(); + + sp<Session> findSession( + const std::vector<uint8_t>& sessionId); + + void destroySession(const sp<Session>& session); + + size_t numOpenSessions() const { return mSessions.size(); } + +private: + CLEARKEY_DISALLOW_COPY_AND_ASSIGN(SessionLibrary); + + SessionLibrary() : mNextSessionId(1) {} + + static Mutex sSingletonLock; + static SessionLibrary* sSingleton; + + Mutex mSessionsLock; + uint32_t mNextSessionId; + std::map<std::vector<uint8_t>, sp<Session> > mSessions; +}; + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android + +#endif // CLEARKEY_SESSION_LIBRARY_H_ diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/TypeConvert.h b/drm/mediadrm/plugins/clearkey/hidl/include/TypeConvert.h new file mode 100644 index 0000000000..cc06329b84 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/include/TypeConvert.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CLEARKEY_ANDROID_HARDWARE_DRM_V1_1_TYPECONVERT +#define CLEARKEY_ANDROID_HARDWARE_DRM_V1_1_TYPECONVERT + +#include <vector> + +#include <android/hardware/drm/1.0/types.h> + +namespace android { +namespace hardware { +namespace drm { +namespace V1_1 { +namespace clearkey { + +using ::android::hardware::hidl_array; +using ::android::hardware::hidl_vec; + +template<typename T> const hidl_vec<T> toHidlVec(const std::vector<T> &vec) { + hidl_vec<T> hVec; + hVec.setToExternal(const_cast<T *>(vec.data()), vec.size()); + return hVec; +} + +template<typename T> hidl_vec<T> toHidlVec(std::vector<T> &vec) { + hidl_vec<T> hVec; + hVec.setToExternal(vec.data(), vec.size()); + return hVec; +} + +template<typename T> const std::vector<T> toVector(const hidl_vec<T> &hVec) { + std::vector<T> vec; + vec.assign(hVec.data(), hVec.data() + hVec.size()); + return *const_cast<const std::vector<T> *>(&vec); +} + +template<typename T> std::vector<T> toVector(hidl_vec<T> &hVec) { + std::vector<T> vec; + vec.assign(hVec.data(), hVec.data() + hVec.size()); + return vec; +} + +template<typename T, size_t SIZE> const std::vector<T> toVector( + const hidl_array<T, SIZE> &hArray) { + std::vector<T> vec; + vec.assign(hArray.data(), hArray.data() + hArray.size()); + return vec; +} + +template<typename T, size_t SIZE> std::vector<T> toVector( + hidl_array<T, SIZE> &hArray) { + std::vector<T> vec; + vec.assign(hArray.data(), hArray.data() + hArray.size()); + return vec; +} + +} // namespace clearkey +} // namespace V1_1 +} // namespace drm +} // namespace hardware +} // namespace android + +#endif // CLEARKEY_ANDROID_HARDWARE_DRM_V1_1_TYPECONVERT diff --git a/drm/mediadrm/plugins/clearkey/hidl/service.cpp b/drm/mediadrm/plugins/clearkey/hidl/service.cpp new file mode 100644 index 0000000000..6a97b720d2 --- /dev/null +++ b/drm/mediadrm/plugins/clearkey/hidl/service.cpp @@ -0,0 +1,54 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define LOG_TAG "android.hardware.drm@1.1-service.clearkey" + +#include <CryptoFactory.h> +#include <DrmFactory.h> + +#include <android-base/logging.h> +#include <binder/ProcessState.h> +#include <hidl/HidlTransportSupport.h> + +using ::android::hardware::configureRpcThreadpool; +using ::android::hardware::joinRpcThreadpool; +using ::android::sp; + +using android::hardware::drm::V1_1::ICryptoFactory; +using android::hardware::drm::V1_1::IDrmFactory; +using android::hardware::drm::V1_1::clearkey::CryptoFactory; +using android::hardware::drm::V1_1::clearkey::DrmFactory; + + +int main(int /* argc */, char** /* argv */) { + ALOGD("android.hardware.drm@1.1-service.clearkey starting..."); + + // The DRM HAL may communicate to other vendor components via + // /dev/vndbinder + android::ProcessState::initWithDriver("/dev/vndbinder"); + + sp<IDrmFactory> drmFactory = new DrmFactory; + sp<ICryptoFactory> cryptoFactory = new CryptoFactory; + + configureRpcThreadpool(8, true /* callerWillJoin */); + + // Setup hwbinder service + CHECK_EQ(drmFactory->registerAsService("clearkey"), android::NO_ERROR) + << "Failed to register Clearkey Factory HAL"; + CHECK_EQ(cryptoFactory->registerAsService("clearkey"), android::NO_ERROR) + << "Failed to register Clearkey Crypto HAL"; + + joinRpcThreadpool(); +} diff --git a/drm/mediadrm/plugins/mock/Android.bp b/drm/mediadrm/plugins/mock/Android.bp index abd18840f3..dd2ad7b454 100644 --- a/drm/mediadrm/plugins/mock/Android.bp +++ b/drm/mediadrm/plugins/mock/Android.bp @@ -32,5 +32,7 @@ cc_library_shared { cflags: [ // Set the following flag to enable the decryption passthru flow //"-DENABLE_PASSTHRU_DECRYPTION", + "-Wall", + "-Werror", ], } |
