Add aidl interface support for Drm Framework

This change intends to make DrmHal/CryptoHal layer support aidl
interface implemented plugins.

Test: CtsMediaDrmTestCases, GtsMediaTestCases

Bug: 200055138
Bug: 170964303
Change-Id: I7056adf184acf1463cb4fc85e1c95ac88c2097f6
diff --git a/drm/libmediadrm/DrmHalHidl.cpp b/drm/libmediadrm/DrmHalHidl.cpp
new file mode 100644
index 0000000..a5dd4d7
--- /dev/null
+++ b/drm/libmediadrm/DrmHalHidl.cpp
@@ -0,0 +1,1516 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "DrmHalHidl"
+
+#include <aidl/android/media/BnResourceManagerClient.h>
+#include <android/binder_manager.h>
+#include <android/hardware/drm/1.2/types.h>
+#include <android/hidl/manager/1.2/IServiceManager.h>
+#include <hidl/ServiceManagement.h>
+#include <media/EventMetric.h>
+#include <media/MediaMetrics.h>
+#include <media/PluginMetricsReporting.h>
+#include <media/drm/DrmAPI.h>
+#include <media/stagefright/MediaErrors.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 <mediadrm/DrmHalHidl.h>
+#include <mediadrm/DrmSessionClientInterface.h>
+#include <mediadrm/DrmSessionManager.h>
+#include <mediadrm/DrmUtils.h>
+#include <mediadrm/IDrmMetricsConsumer.h>
+#include <utils/Log.h>
+
+#include <iomanip>
+#include <vector>
+
+using ::android::sp;
+using ::android::DrmUtils::toStatusT;
+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::hardware::drm::V1_1::DrmMetricGroup;
+using ::android::os::PersistableBundle;
+using drm::V1_0::KeyedVector;
+using drm::V1_0::KeyRequestType;
+using drm::V1_0::KeyType;
+using drm::V1_0::KeyValue;
+using drm::V1_0::SecureStop;
+using drm::V1_0::SecureStopId;
+using drm::V1_0::Status;
+using drm::V1_1::HdcpLevel;
+using drm::V1_1::SecureStopRelease;
+using drm::V1_1::SecurityLevel;
+using drm::V1_2::KeySetId;
+using drm::V1_2::KeyStatusType;
+
+typedef drm::V1_1::KeyRequestType KeyRequestType_V1_1;
+typedef drm::V1_2::Status Status_V1_2;
+typedef drm::V1_2::HdcpLevel HdcpLevel_V1_2;
+
+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 const Vector<uint8_t> toVector(const hidl_vec<uint8_t>& vec) {
+    Vector<uint8_t> vector;
+    vector.appendArray(vec.data(), vec.size());
+    return *const_cast<const Vector<uint8_t>*>(&vector);
+}
+
+static hidl_vec<uint8_t> toHidlVec(const Vector<uint8_t>& vector) {
+    hidl_vec<uint8_t> vec;
+    vec.setToExternal(const_cast<uint8_t*>(vector.array()), vector.size());
+    return vec;
+}
+
+static String8 toString8(const hidl_string& string) {
+    return String8(string.c_str());
+}
+
+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 SecurityLevel toHidlSecurityLevel(DrmPlugin::SecurityLevel level) {
+    switch (level) {
+        case DrmPlugin::kSecurityLevelSwSecureCrypto:
+            return SecurityLevel::SW_SECURE_CRYPTO;
+        case DrmPlugin::kSecurityLevelSwSecureDecode:
+            return SecurityLevel::SW_SECURE_DECODE;
+        case DrmPlugin::kSecurityLevelHwSecureCrypto:
+            return SecurityLevel::HW_SECURE_CRYPTO;
+        case DrmPlugin::kSecurityLevelHwSecureDecode:
+            return SecurityLevel::HW_SECURE_DECODE;
+        case DrmPlugin::kSecurityLevelHwSecureAll:
+            return SecurityLevel::HW_SECURE_ALL;
+        default:
+            return SecurityLevel::UNKNOWN;
+    }
+}
+
+static DrmPlugin::OfflineLicenseState toOfflineLicenseState(OfflineLicenseState licenseState) {
+    switch (licenseState) {
+        case OfflineLicenseState::USABLE:
+            return DrmPlugin::kOfflineLicenseStateUsable;
+        case OfflineLicenseState::INACTIVE:
+            return DrmPlugin::kOfflineLicenseStateReleased;
+        default:
+            return DrmPlugin::kOfflineLicenseStateUnknown;
+    }
+}
+
+static DrmPlugin::HdcpLevel toHdcpLevel(HdcpLevel_V1_2 level) {
+    switch (level) {
+        case HdcpLevel_V1_2::HDCP_NONE:
+            return DrmPlugin::kHdcpNone;
+        case HdcpLevel_V1_2::HDCP_V1:
+            return DrmPlugin::kHdcpV1;
+        case HdcpLevel_V1_2::HDCP_V2:
+            return DrmPlugin::kHdcpV2;
+        case HdcpLevel_V1_2::HDCP_V2_1:
+            return DrmPlugin::kHdcpV2_1;
+        case HdcpLevel_V1_2::HDCP_V2_2:
+            return DrmPlugin::kHdcpV2_2;
+        case HdcpLevel_V1_2::HDCP_V2_3:
+            return DrmPlugin::kHdcpV2_3;
+        case HdcpLevel_V1_2::HDCP_NO_OUTPUT:
+            return DrmPlugin::kHdcpNoOutput;
+        default:
+            return DrmPlugin::kHdcpLevelUnknown;
+    }
+}
+static ::KeyedVector toHidlKeyedVector(const KeyedVector<String8, String8>& keyedVector) {
+    std::vector<KeyValue> stdKeyedVector;
+    for (size_t i = 0; i < keyedVector.size(); i++) {
+        KeyValue keyValue;
+        keyValue.key = toHidlString(keyedVector.keyAt(i));
+        keyValue.value = toHidlString(keyedVector.valueAt(i));
+        stdKeyedVector.push_back(keyValue);
+    }
+    return ::KeyedVector(stdKeyedVector);
+}
+
+static KeyedVector<String8, String8> toKeyedVector(const ::KeyedVector& hKeyedVector) {
+    KeyedVector<String8, String8> keyedVector;
+    for (size_t i = 0; i < hKeyedVector.size(); i++) {
+        keyedVector.add(toString8(hKeyedVector[i].key), toString8(hKeyedVector[i].value));
+    }
+    return keyedVector;
+}
+
+static List<Vector<uint8_t>> toSecureStops(const hidl_vec<SecureStop>& hSecureStops) {
+    List<Vector<uint8_t>> secureStops;
+    for (size_t i = 0; i < hSecureStops.size(); i++) {
+        secureStops.push_back(toVector(hSecureStops[i].opaqueData));
+    }
+    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 List<Vector<uint8_t>> toKeySetIds(const hidl_vec<KeySetId>& hKeySetIds) {
+    List<Vector<uint8_t>> keySetIds;
+    for (size_t i = 0; i < hKeySetIds.size(); i++) {
+        keySetIds.push_back(toVector(hKeySetIds[i]));
+    }
+    return keySetIds;
+}
+
+Mutex DrmHalHidl::mLock;
+
+struct DrmHalHidl::DrmSessionClient : public aidl::android::media::BnResourceManagerClient {
+    explicit DrmSessionClient(DrmHalHidl* drm, const Vector<uint8_t>& sessionId)
+        : mSessionId(sessionId), mDrm(drm) {}
+
+    ::ndk::ScopedAStatus reclaimResource(bool* _aidl_return) override;
+    ::ndk::ScopedAStatus getName(::std::string* _aidl_return) override;
+
+    const Vector<uint8_t> mSessionId;
+
+    virtual ~DrmSessionClient();
+
+  private:
+    wp<DrmHalHidl> mDrm;
+
+    DISALLOW_EVIL_CONSTRUCTORS(DrmSessionClient);
+};
+
+::ndk::ScopedAStatus DrmHalHidl::DrmSessionClient::reclaimResource(bool* _aidl_return) {
+    auto sessionId = mSessionId;
+    sp<DrmHalHidl> drm = mDrm.promote();
+    if (drm == NULL) {
+        *_aidl_return = true;
+        return ::ndk::ScopedAStatus::ok();
+    }
+    status_t err = drm->closeSession(sessionId);
+    if (err != OK) {
+        *_aidl_return = false;
+        return ::ndk::ScopedAStatus::ok();
+    }
+    drm->sendEvent(EventType::SESSION_RECLAIMED, toHidlVec(sessionId), hidl_vec<uint8_t>());
+    *_aidl_return = true;
+    return ::ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus DrmHalHidl::DrmSessionClient::getName(::std::string* _aidl_return) {
+    String8 name;
+    sp<DrmHalHidl> drm = mDrm.promote();
+    if (drm == NULL) {
+        name.append("<deleted>");
+    } else if (drm->getPropertyStringInternal(String8("vendor"), name) != OK || name.isEmpty()) {
+        name.append("<Get vendor failed or is empty>");
+    }
+    name.append("[");
+    for (size_t i = 0; i < mSessionId.size(); ++i) {
+        name.appendFormat("%02x", mSessionId[i]);
+    }
+    name.append("]");
+    *_aidl_return = name;
+    return ::ndk::ScopedAStatus::ok();
+}
+
+DrmHalHidl::DrmSessionClient::~DrmSessionClient() {
+    DrmSessionManager::Instance()->removeSession(mSessionId);
+}
+
+DrmHalHidl::DrmHalHidl()
+    : mFactories(makeDrmFactories()),
+      mInitCheck((mFactories.size() == 0) ? ERROR_UNSUPPORTED : NO_INIT) {}
+
+void DrmHalHidl::closeOpenSessions() {
+    Mutex::Autolock autoLock(mLock);
+    auto openSessions = mOpenSessions;
+    for (size_t i = 0; i < openSessions.size(); i++) {
+        mLock.unlock();
+        closeSession(openSessions[i]->mSessionId);
+        mLock.lock();
+    }
+    mOpenSessions.clear();
+}
+
+DrmHalHidl::~DrmHalHidl() {}
+
+void DrmHalHidl::cleanup() {
+    closeOpenSessions();
+
+    Mutex::Autolock autoLock(mLock);
+    reportFrameworkMetrics(reportPluginMetrics());
+
+    setListener(NULL);
+    mInitCheck = NO_INIT;
+    if (mPluginV1_2 != NULL) {
+        if (!mPluginV1_2->setListener(NULL).isOk()) {
+            mInitCheck = DEAD_OBJECT;
+        }
+    } else if (mPlugin != NULL) {
+        if (!mPlugin->setListener(NULL).isOk()) {
+            mInitCheck = DEAD_OBJECT;
+        }
+    }
+    mPlugin.clear();
+    mPluginV1_1.clear();
+    mPluginV1_2.clear();
+    mPluginV1_4.clear();
+}
+
+std::vector<sp<IDrmFactory>> DrmHalHidl::makeDrmFactories() {
+    static std::vector<sp<IDrmFactory>> factories(DrmUtils::MakeDrmFactories());
+    if (factories.size() == 0) {
+        // must be in passthrough mode, load the default passthrough service
+        auto passthrough = IDrmFactory::getService();
+        if (passthrough != NULL) {
+            DrmUtils::LOG2BI("makeDrmFactories: using default passthrough drm instance");
+            factories.push_back(passthrough);
+        } else {
+            DrmUtils::LOG2BE("Failed to find any drm factories");
+        }
+    }
+    return factories;
+}
+
+sp<IDrmPlugin> DrmHalHidl::makeDrmPlugin(const sp<IDrmFactory>& factory, const uint8_t uuid[16],
+                                         const String8& appPackageName) {
+    mAppPackageName = appPackageName;
+    mMetrics.SetAppPackageName(appPackageName);
+    mMetrics.SetAppUid(AIBinder_getCallingUid());
+
+    sp<IDrmPlugin> plugin;
+    Return<void> hResult = factory->createPlugin(
+            uuid, appPackageName.string(), [&](Status status, const sp<IDrmPlugin>& hPlugin) {
+                if (status != Status::OK) {
+                    DrmUtils::LOG2BE(uuid, "Failed to make drm plugin: %d", status);
+                    return;
+                }
+                plugin = hPlugin;
+            });
+
+    if (!hResult.isOk()) {
+        DrmUtils::LOG2BE(uuid, "createPlugin remote call failed: %s",
+                         hResult.description().c_str());
+    }
+
+    return plugin;
+}
+
+status_t DrmHalHidl::initCheck() const {
+    return mInitCheck;
+}
+
+status_t DrmHalHidl::setListener(const sp<IDrmClient>& listener) {
+    Mutex::Autolock lock(mEventLock);
+    mListener = listener;
+    return NO_ERROR;
+}
+
+Return<void> DrmHalHidl::sendEvent(EventType hEventType, const hidl_vec<uint8_t>& sessionId,
+                                   const hidl_vec<uint8_t>& data) {
+    mMetrics.mEventCounter.Increment((uint32_t)hEventType);
+
+    mEventLock.lock();
+    sp<IDrmClient> listener = mListener;
+    mEventLock.unlock();
+
+    if (listener != NULL) {
+        Mutex::Autolock lock(mNotifyLock);
+        DrmPlugin::EventType eventType;
+        switch (hEventType) {
+            case EventType::PROVISION_REQUIRED:
+                eventType = DrmPlugin::kDrmPluginEventProvisionRequired;
+                break;
+            case EventType::KEY_NEEDED:
+                eventType = DrmPlugin::kDrmPluginEventKeyNeeded;
+                break;
+            case EventType::KEY_EXPIRED:
+                eventType = DrmPlugin::kDrmPluginEventKeyExpired;
+                break;
+            case EventType::VENDOR_DEFINED:
+                eventType = DrmPlugin::kDrmPluginEventVendorDefined;
+                break;
+            case EventType::SESSION_RECLAIMED:
+                eventType = DrmPlugin::kDrmPluginEventSessionReclaimed;
+                break;
+            default:
+                return Void();
+        }
+        listener->sendEvent(eventType, sessionId, data);
+    }
+    return Void();
+}
+
+Return<void> DrmHalHidl::sendExpirationUpdate(const hidl_vec<uint8_t>& sessionId,
+                                              int64_t expiryTimeInMS) {
+    mEventLock.lock();
+    sp<IDrmClient> listener = mListener;
+    mEventLock.unlock();
+
+    if (listener != NULL) {
+        Mutex::Autolock lock(mNotifyLock);
+        listener->sendExpirationUpdate(sessionId, expiryTimeInMS);
+    }
+    return Void();
+}
+
+Return<void> DrmHalHidl::sendKeysChange(const hidl_vec<uint8_t>& sessionId,
+                                        const hidl_vec<KeyStatus_V1_0>& keyStatusList_V1_0,
+                                        bool hasNewUsableKey) {
+    std::vector<KeyStatus> keyStatusVec;
+    for (const auto& keyStatus_V1_0 : keyStatusList_V1_0) {
+        keyStatusVec.push_back(
+                {keyStatus_V1_0.keyId, static_cast<KeyStatusType>(keyStatus_V1_0.type)});
+    }
+    hidl_vec<KeyStatus> keyStatusList_V1_2(keyStatusVec);
+    return sendKeysChange_1_2(sessionId, keyStatusList_V1_2, hasNewUsableKey);
+}
+
+Return<void> DrmHalHidl::sendKeysChange_1_2(const hidl_vec<uint8_t>& sessionId,
+                                            const hidl_vec<KeyStatus>& hKeyStatusList,
+                                            bool hasNewUsableKey) {
+    mEventLock.lock();
+    sp<IDrmClient> listener = mListener;
+    mEventLock.unlock();
+
+    if (listener != NULL) {
+        std::vector<DrmKeyStatus> keyStatusList;
+        size_t nKeys = hKeyStatusList.size();
+        for (size_t i = 0; i < nKeys; ++i) {
+            const KeyStatus& keyStatus = hKeyStatusList[i];
+            uint32_t type;
+            switch (keyStatus.type) {
+                case KeyStatusType::USABLE:
+                    type = DrmPlugin::kKeyStatusType_Usable;
+                    break;
+                case KeyStatusType::EXPIRED:
+                    type = DrmPlugin::kKeyStatusType_Expired;
+                    break;
+                case KeyStatusType::OUTPUTNOTALLOWED:
+                    type = DrmPlugin::kKeyStatusType_OutputNotAllowed;
+                    break;
+                case KeyStatusType::STATUSPENDING:
+                    type = DrmPlugin::kKeyStatusType_StatusPending;
+                    break;
+                case KeyStatusType::USABLEINFUTURE:
+                    type = DrmPlugin::kKeyStatusType_UsableInFuture;
+                    break;
+                case KeyStatusType::INTERNALERROR:
+                default:
+                    type = DrmPlugin::kKeyStatusType_InternalError;
+                    break;
+            }
+            keyStatusList.push_back({type, keyStatus.keyId});
+            mMetrics.mKeyStatusChangeCounter.Increment((uint32_t)keyStatus.type);
+        }
+
+        Mutex::Autolock lock(mNotifyLock);
+        listener->sendKeysChange(sessionId, keyStatusList, hasNewUsableKey);
+    } else {
+        // There's no listener. But we still want to count the key change
+        // events.
+        size_t nKeys = hKeyStatusList.size();
+        for (size_t i = 0; i < nKeys; i++) {
+            mMetrics.mKeyStatusChangeCounter.Increment((uint32_t)hKeyStatusList[i].type);
+        }
+    }
+
+    return Void();
+}
+
+Return<void> DrmHalHidl::sendSessionLostState(const hidl_vec<uint8_t>& sessionId) {
+    mEventLock.lock();
+    sp<IDrmClient> listener = mListener;
+    mEventLock.unlock();
+
+    if (listener != NULL) {
+        Mutex::Autolock lock(mNotifyLock);
+        listener->sendSessionLostState(sessionId);
+    }
+    return Void();
+}
+
+status_t DrmHalHidl::matchMimeTypeAndSecurityLevel(const sp<IDrmFactory>& factory,
+                                                   const uint8_t uuid[16], const String8& mimeType,
+                                                   DrmPlugin::SecurityLevel level,
+                                                   bool* isSupported) {
+    *isSupported = false;
+
+    // handle default value cases
+    if (level == DrmPlugin::kSecurityLevelUnknown) {
+        if (mimeType == "") {
+            // isCryptoSchemeSupported(uuid)
+            *isSupported = true;
+        } else {
+            // isCryptoSchemeSupported(uuid, mimeType)
+            *isSupported = factory->isContentTypeSupported(mimeType.string());
+        }
+        return OK;
+    } else if (mimeType == "") {
+        return BAD_VALUE;
+    }
+
+    sp<drm::V1_2::IDrmFactory> factoryV1_2 = drm::V1_2::IDrmFactory::castFrom(factory);
+    if (factoryV1_2 == NULL) {
+        return ERROR_UNSUPPORTED;
+    } else {
+        *isSupported = factoryV1_2->isCryptoSchemeSupported_1_2(uuid, mimeType.string(),
+                                                                toHidlSecurityLevel(level));
+        return OK;
+    }
+}
+
+status_t DrmHalHidl::isCryptoSchemeSupported(const uint8_t uuid[16], const String8& mimeType,
+                                             DrmPlugin::SecurityLevel level, bool* isSupported) {
+    Mutex::Autolock autoLock(mLock);
+    *isSupported = false;
+    for (ssize_t i = mFactories.size() - 1; i >= 0; i--) {
+        if (mFactories[i]->isCryptoSchemeSupported(uuid)) {
+            return matchMimeTypeAndSecurityLevel(mFactories[i], uuid, mimeType, level, isSupported);
+        }
+    }
+    return OK;
+}
+
+status_t DrmHalHidl::createPlugin(const uint8_t uuid[16], const String8& appPackageName) {
+    Mutex::Autolock autoLock(mLock);
+
+    for (ssize_t i = mFactories.size() - 1; i >= 0; i--) {
+        auto hResult = mFactories[i]->isCryptoSchemeSupported(uuid);
+        if (hResult.isOk() && hResult) {
+            auto plugin = makeDrmPlugin(mFactories[i], uuid, appPackageName);
+            if (plugin != NULL) {
+                mPlugin = plugin;
+                mPluginV1_1 = drm::V1_1::IDrmPlugin::castFrom(mPlugin);
+                mPluginV1_2 = drm::V1_2::IDrmPlugin::castFrom(mPlugin);
+                mPluginV1_4 = drm::V1_4::IDrmPlugin::castFrom(mPlugin);
+                break;
+            }
+        }
+    }
+
+    if (mPlugin == NULL) {
+        DrmUtils::LOG2BE(uuid, "No supported hal instance found");
+        mInitCheck = ERROR_UNSUPPORTED;
+    } else {
+        mInitCheck = OK;
+        if (mPluginV1_2 != NULL) {
+            if (!mPluginV1_2->setListener(this).isOk()) {
+                mInitCheck = DEAD_OBJECT;
+            }
+        } else if (!mPlugin->setListener(this).isOk()) {
+            mInitCheck = DEAD_OBJECT;
+        }
+        if (mInitCheck != OK) {
+            mPlugin.clear();
+            mPluginV1_1.clear();
+            mPluginV1_2.clear();
+            mPluginV1_4.clear();
+        }
+    }
+
+    return mInitCheck;
+}
+
+status_t DrmHalHidl::destroyPlugin() {
+    cleanup();
+    return OK;
+}
+
+status_t DrmHalHidl::openSession(DrmPlugin::SecurityLevel level, Vector<uint8_t>& sessionId) {
+    Mutex::Autolock autoLock(mLock);
+    INIT_CHECK();
+
+    SecurityLevel hSecurityLevel = toHidlSecurityLevel(level);
+    bool setSecurityLevel = true;
+
+    if (level == DrmPlugin::kSecurityLevelMax) {
+        setSecurityLevel = false;
+    } else {
+        if (hSecurityLevel == SecurityLevel::UNKNOWN) {
+            return ERROR_DRM_CANNOT_HANDLE;
+        }
+    }
+
+    status_t err = UNKNOWN_ERROR;
+    bool retry = true;
+    do {
+        hidl_vec<uint8_t> hSessionId;
+
+        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);
+            });
+        } 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;
+        }
+
+        if (err == ERROR_DRM_RESOURCE_BUSY && retry) {
+            mLock.unlock();
+            // reclaimSession may call back to closeSession, since mLock is
+            // shared between Drm instances, we should unlock here to avoid
+            // deadlock.
+            retry = DrmSessionManager::Instance()->reclaimSession(AIBinder_getCallingPid());
+            mLock.lock();
+        } else {
+            retry = false;
+        }
+    } while (retry);
+
+    if (err == OK) {
+        std::shared_ptr<DrmSessionClient> client =
+                ndk::SharedRefBase::make<DrmSessionClient>(this, sessionId);
+        DrmSessionManager::Instance()->addSession(
+                AIBinder_getCallingPid(), std::static_pointer_cast<IResourceManagerClient>(client),
+                sessionId);
+        mOpenSessions.push_back(client);
+        mMetrics.SetSessionStart(sessionId);
+    }
+
+    mMetrics.mOpenSessionCounter.Increment(err);
+    return err;
+}
+
+status_t DrmHalHidl::closeSession(Vector<uint8_t> const& sessionId) {
+    Mutex::Autolock autoLock(mLock);
+    INIT_CHECK();
+
+    Return<Status> status = mPlugin->closeSession(toHidlVec(sessionId));
+    if (status.isOk()) {
+        if (status == Status::OK) {
+            DrmSessionManager::Instance()->removeSession(sessionId);
+            for (auto i = mOpenSessions.begin(); i != mOpenSessions.end(); i++) {
+                if (isEqualSessionId((*i)->mSessionId, sessionId)) {
+                    mOpenSessions.erase(i);
+                    break;
+                }
+            }
+        }
+        status_t response = toStatusT(status);
+        mMetrics.SetSessionEnd(sessionId);
+        mMetrics.mCloseSessionCounter.Increment(response);
+        return response;
+    }
+    mMetrics.mCloseSessionCounter.Increment(DEAD_OBJECT);
+    return DEAD_OBJECT;
+}
+
+static DrmPlugin::KeyRequestType toKeyRequestType(KeyRequestType keyRequestType) {
+    switch (keyRequestType) {
+        case KeyRequestType::INITIAL:
+            return DrmPlugin::kKeyRequestType_Initial;
+            break;
+        case KeyRequestType::RENEWAL:
+            return DrmPlugin::kKeyRequestType_Renewal;
+            break;
+        case KeyRequestType::RELEASE:
+            return DrmPlugin::kKeyRequestType_Release;
+            break;
+        default:
+            return DrmPlugin::kKeyRequestType_Unknown;
+            break;
+    }
+}
+
+static DrmPlugin::KeyRequestType toKeyRequestType_1_1(KeyRequestType_V1_1 keyRequestType) {
+    switch (keyRequestType) {
+        case KeyRequestType_V1_1::NONE:
+            return DrmPlugin::kKeyRequestType_None;
+            break;
+        case KeyRequestType_V1_1::UPDATE:
+            return DrmPlugin::kKeyRequestType_Update;
+            break;
+        default:
+            return toKeyRequestType(static_cast<KeyRequestType>(keyRequestType));
+            break;
+    }
+}
+
+status_t DrmHalHidl::getKeyRequest(Vector<uint8_t> const& sessionId,
+                                   Vector<uint8_t> const& initData, String8 const& mimeType,
+                                   DrmPlugin::KeyType keyType,
+                                   KeyedVector<String8, String8> const& optionalParameters,
+                                   Vector<uint8_t>& request, String8& defaultUrl,
+                                   DrmPlugin::KeyRequestType* keyRequestType) {
+    Mutex::Autolock autoLock(mLock);
+    INIT_CHECK();
+    EventTimer<status_t> keyRequestTimer(&mMetrics.mGetKeyRequestTimeUs);
+
+    DrmSessionManager::Instance()->useSession(sessionId);
+
+    KeyType hKeyType;
+    if (keyType == DrmPlugin::kKeyType_Streaming) {
+        hKeyType = KeyType::STREAMING;
+    } else if (keyType == DrmPlugin::kKeyType_Offline) {
+        hKeyType = KeyType::OFFLINE;
+    } else if (keyType == DrmPlugin::kKeyType_Release) {
+        hKeyType = KeyType::RELEASE;
+    } else {
+        keyRequestTimer.SetAttribute(BAD_VALUE);
+        return BAD_VALUE;
+    }
+
+    ::KeyedVector hOptionalParameters = toHidlKeyedVector(optionalParameters);
+
+    status_t err = UNKNOWN_ERROR;
+    Return<void> hResult;
+
+    if (mPluginV1_2 != NULL) {
+        hResult = mPluginV1_2->getKeyRequest_1_2(
+                toHidlVec(sessionId), toHidlVec(initData), toHidlString(mimeType), hKeyType,
+                hOptionalParameters,
+                [&](Status_V1_2 status, const hidl_vec<uint8_t>& hRequest,
+                    KeyRequestType_V1_1 hKeyRequestType, const hidl_string& hDefaultUrl) {
+                    if (status == Status_V1_2::OK) {
+                        request = toVector(hRequest);
+                        defaultUrl = toString8(hDefaultUrl);
+                        *keyRequestType = toKeyRequestType_1_1(hKeyRequestType);
+                    }
+                    err = toStatusT(status);
+                });
+    } else if (mPluginV1_1 != NULL) {
+        hResult = mPluginV1_1->getKeyRequest_1_1(
+                toHidlVec(sessionId), toHidlVec(initData), toHidlString(mimeType), hKeyType,
+                hOptionalParameters,
+                [&](Status status, const hidl_vec<uint8_t>& hRequest,
+                    KeyRequestType_V1_1 hKeyRequestType, const hidl_string& hDefaultUrl) {
+                    if (status == Status::OK) {
+                        request = toVector(hRequest);
+                        defaultUrl = toString8(hDefaultUrl);
+                        *keyRequestType = toKeyRequestType_1_1(hKeyRequestType);
+                    }
+                    err = toStatusT(status);
+                });
+    } else {
+        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) {
+                    if (status == Status::OK) {
+                        request = toVector(hRequest);
+                        defaultUrl = toString8(hDefaultUrl);
+                        *keyRequestType = toKeyRequestType(hKeyRequestType);
+                    }
+                    err = toStatusT(status);
+                });
+    }
+
+    err = hResult.isOk() ? err : DEAD_OBJECT;
+    keyRequestTimer.SetAttribute(err);
+    return err;
+}
+
+status_t DrmHalHidl::provideKeyResponse(Vector<uint8_t> const& sessionId,
+                                        Vector<uint8_t> const& response,
+                                        Vector<uint8_t>& keySetId) {
+    Mutex::Autolock autoLock(mLock);
+    INIT_CHECK();
+    EventTimer<status_t> keyResponseTimer(&mMetrics.mProvideKeyResponseTimeUs);
+
+    DrmSessionManager::Instance()->useSession(sessionId);
+
+    status_t err = UNKNOWN_ERROR;
+
+    Return<void> hResult =
+            mPlugin->provideKeyResponse(toHidlVec(sessionId), toHidlVec(response),
+                                        [&](Status status, const hidl_vec<uint8_t>& hKeySetId) {
+                                            if (status == Status::OK) {
+                                                keySetId = toVector(hKeySetId);
+                                            }
+                                            err = toStatusT(status);
+                                        });
+    err = hResult.isOk() ? err : DEAD_OBJECT;
+    keyResponseTimer.SetAttribute(err);
+    return err;
+}
+
+status_t DrmHalHidl::removeKeys(Vector<uint8_t> const& keySetId) {
+    Mutex::Autolock autoLock(mLock);
+    INIT_CHECK();
+
+    Return<Status> status = mPlugin->removeKeys(toHidlVec(keySetId));
+    return status.isOk() ? toStatusT(status) : DEAD_OBJECT;
+}
+
+status_t DrmHalHidl::restoreKeys(Vector<uint8_t> const& sessionId,
+                                 Vector<uint8_t> const& keySetId) {
+    Mutex::Autolock autoLock(mLock);
+    INIT_CHECK();
+
+    DrmSessionManager::Instance()->useSession(sessionId);
+
+    Return<Status> status = mPlugin->restoreKeys(toHidlVec(sessionId), toHidlVec(keySetId));
+    return status.isOk() ? toStatusT(status) : DEAD_OBJECT;
+}
+
+status_t DrmHalHidl::queryKeyStatus(Vector<uint8_t> const& sessionId,
+                                    KeyedVector<String8, String8>& infoMap) const {
+    Mutex::Autolock autoLock(mLock);
+    INIT_CHECK();
+
+    DrmSessionManager::Instance()->useSession(sessionId);
+
+    ::KeyedVector hInfoMap;
+
+    status_t err = UNKNOWN_ERROR;
+
+    Return<void> hResult = mPlugin->queryKeyStatus(
+            toHidlVec(sessionId), [&](Status status, const hidl_vec<KeyValue>& map) {
+                if (status == Status::OK) {
+                    infoMap = toKeyedVector(map);
+                }
+                err = toStatusT(status);
+            });
+
+    return hResult.isOk() ? err : DEAD_OBJECT;
+}
+
+status_t DrmHalHidl::getProvisionRequest(String8 const& certType, String8 const& certAuthority,
+                                         Vector<uint8_t>& request, String8& defaultUrl) {
+    Mutex::Autolock autoLock(mLock);
+    INIT_CHECK();
+
+    status_t err = UNKNOWN_ERROR;
+    Return<void> hResult;
+
+    if (mPluginV1_2 != NULL) {
+        hResult = mPluginV1_2->getProvisionRequest_1_2(
+                toHidlString(certType), toHidlString(certAuthority),
+                [&](Status_V1_2 status, const hidl_vec<uint8_t>& hRequest,
+                    const hidl_string& hDefaultUrl) {
+                    if (status == Status_V1_2::OK) {
+                        request = toVector(hRequest);
+                        defaultUrl = toString8(hDefaultUrl);
+                    }
+                    err = toStatusT(status);
+                });
+    } else {
+        hResult = mPlugin->getProvisionRequest(toHidlString(certType), toHidlString(certAuthority),
+                                               [&](Status status, const hidl_vec<uint8_t>& hRequest,
+                                                   const hidl_string& hDefaultUrl) {
+                                                   if (status == Status::OK) {
+                                                       request = toVector(hRequest);
+                                                       defaultUrl = toString8(hDefaultUrl);
+                                                   }
+                                                   err = toStatusT(status);
+                                               });
+    }
+
+    err = hResult.isOk() ? err : DEAD_OBJECT;
+    mMetrics.mGetProvisionRequestCounter.Increment(err);
+    return err;
+}
+
+status_t DrmHalHidl::provideProvisionResponse(Vector<uint8_t> const& response,
+                                              Vector<uint8_t>& certificate,
+                                              Vector<uint8_t>& wrappedKey) {
+    Mutex::Autolock autoLock(mLock);
+    INIT_CHECK();
+
+    status_t err = UNKNOWN_ERROR;
+
+    Return<void> hResult = mPlugin->provideProvisionResponse(
+            toHidlVec(response), [&](Status status, const hidl_vec<uint8_t>& hCertificate,
+                                     const hidl_vec<uint8_t>& hWrappedKey) {
+                if (status == Status::OK) {
+                    certificate = toVector(hCertificate);
+                    wrappedKey = toVector(hWrappedKey);
+                }
+                err = toStatusT(status);
+            });
+
+    err = hResult.isOk() ? err : DEAD_OBJECT;
+    mMetrics.mProvideProvisionResponseCounter.Increment(err);
+    return err;
+}
+
+status_t DrmHalHidl::getSecureStops(List<Vector<uint8_t>>& secureStops) {
+    Mutex::Autolock autoLock(mLock);
+    INIT_CHECK();
+
+    status_t err = UNKNOWN_ERROR;
+
+    Return<void> hResult =
+            mPlugin->getSecureStops([&](Status status, const hidl_vec<SecureStop>& hSecureStops) {
+                if (status == Status::OK) {
+                    secureStops = toSecureStops(hSecureStops);
+                }
+                err = toStatusT(status);
+            });
+
+    return hResult.isOk() ? err : DEAD_OBJECT;
+}
+
+status_t DrmHalHidl::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 DrmHalHidl::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), [&](Status status, const SecureStop& hSecureStop) {
+                if (status == Status::OK) {
+                    secureStop = toVector(hSecureStop.opaqueData);
+                }
+                err = toStatusT(status);
+            });
+
+    return hResult.isOk() ? err : DEAD_OBJECT;
+}
+
+status_t DrmHalHidl::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 DrmHalHidl::removeSecureStop(Vector<uint8_t> const& ssid) {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mInitCheck != OK) {
+        return mInitCheck;
+    }
+
+    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 DrmHalHidl::removeAllSecureStops() {
+    Mutex::Autolock autoLock(mLock);
+    INIT_CHECK();
+
+    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 DrmHalHidl::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;
+
+    *connected = DrmPlugin::kHdcpLevelUnknown;
+    *max = DrmPlugin::kHdcpLevelUnknown;
+
+    Return<void> hResult;
+    if (mPluginV1_2 != NULL) {
+        hResult = mPluginV1_2->getHdcpLevels_1_2([&](Status_V1_2 status,
+                                                     const HdcpLevel_V1_2& hConnected,
+                                                     const HdcpLevel_V1_2& hMax) {
+            if (status == Status_V1_2::OK) {
+                *connected = toHdcpLevel(hConnected);
+                *max = toHdcpLevel(hMax);
+            }
+            err = toStatusT(status);
+        });
+    } else if (mPluginV1_1 != NULL) {
+        hResult = mPluginV1_1->getHdcpLevels(
+                [&](Status status, const HdcpLevel& hConnected, const HdcpLevel& hMax) {
+                    if (status == Status::OK) {
+                        *connected = toHdcpLevel(static_cast<HdcpLevel_V1_2>(hConnected));
+                        *max = toHdcpLevel(static_cast<HdcpLevel_V1_2>(hMax));
+                    }
+                    err = toStatusT(status);
+                });
+    } else {
+        return ERROR_DRM_CANNOT_HANDLE;
+    }
+
+    return hResult.isOk() ? err : DEAD_OBJECT;
+}
+
+status_t DrmHalHidl::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 DrmHalHidl::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 DrmHalHidl::getOfflineLicenseKeySetIds(List<Vector<uint8_t>>& keySetIds) const {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mInitCheck != OK) {
+        return mInitCheck;
+    }
+
+    if (mPluginV1_2 == NULL) {
+        return ERROR_UNSUPPORTED;
+    }
+
+    status_t err = UNKNOWN_ERROR;
+
+    Return<void> hResult = mPluginV1_2->getOfflineLicenseKeySetIds(
+            [&](Status status, const hidl_vec<KeySetId>& hKeySetIds) {
+                if (status == Status::OK) {
+                    keySetIds = toKeySetIds(hKeySetIds);
+                }
+                err = toStatusT(status);
+            });
+
+    return hResult.isOk() ? err : DEAD_OBJECT;
+}
+
+status_t DrmHalHidl::removeOfflineLicense(Vector<uint8_t> const& keySetId) {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mInitCheck != OK) {
+        return mInitCheck;
+    }
+
+    if (mPluginV1_2 == NULL) {
+        return ERROR_UNSUPPORTED;
+    }
+
+    Return<Status> status = mPluginV1_2->removeOfflineLicense(toHidlVec(keySetId));
+    return status.isOk() ? toStatusT(status) : DEAD_OBJECT;
+}
+
+status_t DrmHalHidl::getOfflineLicenseState(Vector<uint8_t> const& keySetId,
+                                            DrmPlugin::OfflineLicenseState* licenseState) const {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mInitCheck != OK) {
+        return mInitCheck;
+    }
+
+    if (mPluginV1_2 == NULL) {
+        return ERROR_UNSUPPORTED;
+    }
+    *licenseState = DrmPlugin::kOfflineLicenseStateUnknown;
+
+    status_t err = UNKNOWN_ERROR;
+
+    Return<void> hResult = mPluginV1_2->getOfflineLicenseState(
+            toHidlVec(keySetId), [&](Status status, OfflineLicenseState hLicenseState) {
+                if (status == Status::OK) {
+                    *licenseState = toOfflineLicenseState(hLicenseState);
+                }
+                err = toStatusT(status);
+            });
+
+    return hResult.isOk() ? err : DEAD_OBJECT;
+}
+
+status_t DrmHalHidl::getPropertyString(String8 const& name, String8& value) const {
+    Mutex::Autolock autoLock(mLock);
+    return getPropertyStringInternal(name, value);
+}
+
+status_t DrmHalHidl::getPropertyStringInternal(String8 const& name, String8& value) const {
+    // This function is internal to the class and should only be called while
+    // mLock is already held.
+    INIT_CHECK();
+
+    status_t err = UNKNOWN_ERROR;
+
+    Return<void> hResult = mPlugin->getPropertyString(
+            toHidlString(name), [&](Status status, const hidl_string& hValue) {
+                if (status == Status::OK) {
+                    value = toString8(hValue);
+                }
+                err = toStatusT(status);
+            });
+
+    return hResult.isOk() ? err : DEAD_OBJECT;
+}
+
+status_t DrmHalHidl::getPropertyByteArray(String8 const& name, Vector<uint8_t>& value) const {
+    Mutex::Autolock autoLock(mLock);
+    return getPropertyByteArrayInternal(name, value);
+}
+
+status_t DrmHalHidl::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.
+    INIT_CHECK();
+
+    status_t err = UNKNOWN_ERROR;
+
+    Return<void> hResult = mPlugin->getPropertyByteArray(
+            toHidlString(name), [&](Status status, const hidl_vec<uint8_t>& hValue) {
+                if (status == Status::OK) {
+                    value = toVector(hValue);
+                }
+                err = toStatusT(status);
+            });
+
+    err = hResult.isOk() ? err : DEAD_OBJECT;
+    if (name == kPropertyDeviceUniqueId) {
+        mMetrics.mGetDeviceUniqueIdCounter.Increment(err);
+    }
+    return err;
+}
+
+status_t DrmHalHidl::setPropertyString(String8 const& name, String8 const& value) const {
+    Mutex::Autolock autoLock(mLock);
+    INIT_CHECK();
+
+    Return<Status> status = mPlugin->setPropertyString(toHidlString(name), toHidlString(value));
+    return status.isOk() ? toStatusT(status) : DEAD_OBJECT;
+}
+
+status_t DrmHalHidl::setPropertyByteArray(String8 const& name, Vector<uint8_t> const& value) const {
+    Mutex::Autolock autoLock(mLock);
+    INIT_CHECK();
+
+    Return<Status> status = mPlugin->setPropertyByteArray(toHidlString(name), toHidlVec(value));
+    return status.isOk() ? toStatusT(status) : DEAD_OBJECT;
+}
+
+status_t DrmHalHidl::getMetrics(const sp<IDrmMetricsConsumer>& consumer) {
+    if (consumer == nullptr) {
+        return UNEXPECTED_NULL;
+    }
+    consumer->consumeFrameworkMetrics(mMetrics);
+
+    // 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 {
+                        consumer->consumeHidlMetrics(vendor, pluginMetrics);
+                    }
+                    err = toStatusT(status);
+                });
+        return status.isOk() ? err : DEAD_OBJECT;
+    }
+
+    return OK;
+}
+
+status_t DrmHalHidl::setCipherAlgorithm(Vector<uint8_t> const& sessionId,
+                                        String8 const& algorithm) {
+    Mutex::Autolock autoLock(mLock);
+    INIT_CHECK();
+
+    DrmSessionManager::Instance()->useSession(sessionId);
+
+    Return<Status> status =
+            mPlugin->setCipherAlgorithm(toHidlVec(sessionId), toHidlString(algorithm));
+    return status.isOk() ? toStatusT(status) : DEAD_OBJECT;
+}
+
+status_t DrmHalHidl::setMacAlgorithm(Vector<uint8_t> const& sessionId, String8 const& algorithm) {
+    Mutex::Autolock autoLock(mLock);
+    INIT_CHECK();
+
+    DrmSessionManager::Instance()->useSession(sessionId);
+
+    Return<Status> status = mPlugin->setMacAlgorithm(toHidlVec(sessionId), toHidlString(algorithm));
+    return status.isOk() ? toStatusT(status) : DEAD_OBJECT;
+}
+
+status_t DrmHalHidl::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);
+    INIT_CHECK();
+
+    DrmSessionManager::Instance()->useSession(sessionId);
+
+    status_t err = UNKNOWN_ERROR;
+
+    Return<void> hResult =
+            mPlugin->encrypt(toHidlVec(sessionId), toHidlVec(keyId), toHidlVec(input),
+                             toHidlVec(iv), [&](Status status, const hidl_vec<uint8_t>& hOutput) {
+                                 if (status == Status::OK) {
+                                     output = toVector(hOutput);
+                                 }
+                                 err = toStatusT(status);
+                             });
+
+    return hResult.isOk() ? err : DEAD_OBJECT;
+}
+
+status_t DrmHalHidl::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);
+    INIT_CHECK();
+
+    DrmSessionManager::Instance()->useSession(sessionId);
+
+    status_t err = UNKNOWN_ERROR;
+
+    Return<void> hResult =
+            mPlugin->decrypt(toHidlVec(sessionId), toHidlVec(keyId), toHidlVec(input),
+                             toHidlVec(iv), [&](Status status, const hidl_vec<uint8_t>& hOutput) {
+                                 if (status == Status::OK) {
+                                     output = toVector(hOutput);
+                                 }
+                                 err = toStatusT(status);
+                             });
+
+    return hResult.isOk() ? err : DEAD_OBJECT;
+}
+
+status_t DrmHalHidl::sign(Vector<uint8_t> const& sessionId, Vector<uint8_t> const& keyId,
+                          Vector<uint8_t> const& message, Vector<uint8_t>& signature) {
+    Mutex::Autolock autoLock(mLock);
+    INIT_CHECK();
+
+    DrmSessionManager::Instance()->useSession(sessionId);
+
+    status_t err = UNKNOWN_ERROR;
+
+    Return<void> hResult = mPlugin->sign(toHidlVec(sessionId), toHidlVec(keyId), toHidlVec(message),
+                                         [&](Status status, const hidl_vec<uint8_t>& hSignature) {
+                                             if (status == Status::OK) {
+                                                 signature = toVector(hSignature);
+                                             }
+                                             err = toStatusT(status);
+                                         });
+
+    return hResult.isOk() ? err : DEAD_OBJECT;
+}
+
+status_t DrmHalHidl::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);
+    INIT_CHECK();
+
+    DrmSessionManager::Instance()->useSession(sessionId);
+
+    status_t err = UNKNOWN_ERROR;
+
+    Return<void> hResult =
+            mPlugin->verify(toHidlVec(sessionId), toHidlVec(keyId), toHidlVec(message),
+                            toHidlVec(signature), [&](Status status, bool hMatch) {
+                                if (status == Status::OK) {
+                                    match = hMatch;
+                                } else {
+                                    match = false;
+                                }
+                                err = toStatusT(status);
+                            });
+
+    return hResult.isOk() ? err : DEAD_OBJECT;
+}
+
+status_t DrmHalHidl::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);
+    INIT_CHECK();
+
+    DrmSessionManager::Instance()->useSession(sessionId);
+
+    status_t err = UNKNOWN_ERROR;
+
+    Return<void> hResult = mPlugin->signRSA(
+            toHidlVec(sessionId), toHidlString(algorithm), toHidlVec(message),
+            toHidlVec(wrappedKey), [&](Status status, const hidl_vec<uint8_t>& hSignature) {
+                if (status == Status::OK) {
+                    signature = toVector(hSignature);
+                }
+                err = toStatusT(status);
+            });
+
+    return hResult.isOk() ? err : DEAD_OBJECT;
+}
+
+std::string DrmHalHidl::reportFrameworkMetrics(const std::string& pluginMetrics) const {
+    mediametrics_handle_t item(mediametrics_create("mediadrm"));
+    mediametrics_setUid(item, mMetrics.GetAppUid());
+    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 {
+        mediametrics_setCString(item, "vendor", vendor.c_str());
+    }
+    result = getPropertyStringInternal(String8("description"), description);
+    if (result != OK) {
+        ALOGE("Failed to get description from drm plugin: %d", result);
+    } else {
+        mediametrics_setCString(item, "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()) {
+        mediametrics_setCString(item, "serialized_metrics", b64EncodedMetrics.c_str());
+    }
+    if (!pluginMetrics.empty()) {
+        mediametrics_setCString(item, "plugin_metrics", pluginMetrics.c_str());
+    }
+    if (!mediametrics_selfRecord(item)) {
+        ALOGE("Failed to self record framework metrics");
+    }
+    mediametrics_delete(item);
+    return serializedMetrics;
+}
+
+std::string DrmHalHidl::reportPluginMetrics() const {
+    Vector<uint8_t> metricsVector;
+    String8 vendor;
+    String8 description;
+    std::string metricsString;
+    if (getPropertyStringInternal(String8("vendor"), vendor) == OK &&
+        getPropertyStringInternal(String8("description"), description) == OK &&
+        getPropertyByteArrayInternal(String8("metrics"), metricsVector) == OK) {
+        metricsString = toBase64StringNoPad(metricsVector.array(), metricsVector.size());
+        status_t res = android::reportDrmPluginMetrics(metricsString, vendor, description,
+                                                       mMetrics.GetAppUid());
+        if (res != OK) {
+            ALOGE("Metrics were retrieved but could not be reported: %d", res);
+        }
+    }
+    return metricsString;
+}
+
+status_t DrmHalHidl::requiresSecureDecoder(const char* mime, bool* required) const {
+    Mutex::Autolock autoLock(mLock);
+    if (mPluginV1_4 == NULL) {
+        return false;
+    }
+    auto hResult = mPluginV1_4->requiresSecureDecoderDefault(hidl_string(mime));
+    if (!hResult.isOk()) {
+        DrmUtils::LOG2BE("requiresSecureDecoder txn failed: %s", hResult.description().c_str());
+        return DEAD_OBJECT;
+    }
+    if (required) {
+        *required = hResult;
+    }
+    return OK;
+}
+
+status_t DrmHalHidl::requiresSecureDecoder(const char* mime, DrmPlugin::SecurityLevel securityLevel,
+                                           bool* required) const {
+    Mutex::Autolock autoLock(mLock);
+    if (mPluginV1_4 == NULL) {
+        return false;
+    }
+    auto hLevel = toHidlSecurityLevel(securityLevel);
+    auto hResult = mPluginV1_4->requiresSecureDecoder(hidl_string(mime), hLevel);
+    if (!hResult.isOk()) {
+        DrmUtils::LOG2BE("requiresSecureDecoder txn failed: %s", hResult.description().c_str());
+        return DEAD_OBJECT;
+    }
+    if (required) {
+        *required = hResult;
+    }
+    return OK;
+}
+
+status_t DrmHalHidl::setPlaybackId(Vector<uint8_t> const& sessionId, const char* playbackId) {
+    Mutex::Autolock autoLock(mLock);
+    if (mPluginV1_4 == NULL) {
+        return ERROR_UNSUPPORTED;
+    }
+    auto err = mPluginV1_4->setPlaybackId(toHidlVec(sessionId), hidl_string(playbackId));
+    return err.isOk() ? toStatusT(err) : DEAD_OBJECT;
+}
+
+status_t DrmHalHidl::getLogMessages(Vector<drm::V1_4::LogMessage>& logs) const {
+    Mutex::Autolock autoLock(mLock);
+    return DrmUtils::GetLogMessages<drm::V1_4::IDrmPlugin>(mPlugin, logs);
+}
+
+}  // namespace android