Add DRM session manager.

Bug: 19265536
Change-Id: Ia9f2c94c64ed6c1fe99d54de81d71c8973994865
diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk
index 9d8fe62..4b31715 100644
--- a/media/libmediaplayerservice/Android.mk
+++ b/media/libmediaplayerservice/Android.mk
@@ -10,6 +10,7 @@
     ActivityManager.cpp         \
     Crypto.cpp                  \
     Drm.cpp                     \
+    DrmSessionManager.cpp       \
     HDCP.cpp                    \
     MediaPlayerFactory.cpp      \
     MediaPlayerService.cpp      \
diff --git a/media/libmediaplayerservice/DrmSessionClientInterface.h b/media/libmediaplayerservice/DrmSessionClientInterface.h
new file mode 100644
index 0000000..17faf08
--- /dev/null
+++ b/media/libmediaplayerservice/DrmSessionClientInterface.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2015 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 DRM_PROXY_INTERFACE_H_
+#define DRM_PROXY_INTERFACE_H_
+
+#include <utils/RefBase.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+struct DrmSessionClientInterface : public RefBase {
+    virtual bool reclaimSession(const Vector<uint8_t>& sessionId) = 0;
+
+protected:
+    virtual ~DrmSessionClientInterface() {}
+};
+
+}  // namespace android
+
+#endif  // DRM_PROXY_INTERFACE_H_
diff --git a/media/libmediaplayerservice/DrmSessionManager.cpp b/media/libmediaplayerservice/DrmSessionManager.cpp
new file mode 100644
index 0000000..43346e0
--- /dev/null
+++ b/media/libmediaplayerservice/DrmSessionManager.cpp
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2015 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 "DrmSessionManager"
+#include <utils/Log.h>
+
+#include "DrmSessionManager.h"
+
+#include "DrmSessionClientInterface.h"
+#include "ProcessInfoInterface.h"
+#include <binder/IPCThreadState.h>
+#include <unistd.h>
+#include <utils/String8.h>
+
+namespace android {
+
+static String8 GetSessionIdString(const Vector<uint8_t> &sessionId) {
+    String8 sessionIdStr;
+    for (size_t i = 0; i < sessionId.size(); ++i) {
+        sessionIdStr.appendFormat("%u ", sessionId[i]);
+    }
+    return sessionIdStr;
+}
+
+struct ProcessInfo : public ProcessInfoInterface {
+    ProcessInfo() {}
+
+    virtual int getPriority(int pid) {
+        // TODO: implement
+        // Get process state to determine priority.
+        // According to the define of PROCESS_STATE_***, higher the value lower
+        // the priority. So we will do a converting from state to priority here.
+        return -1;
+    }
+
+protected:
+    virtual ~ProcessInfo() {}
+
+private:
+    DISALLOW_EVIL_CONSTRUCTORS(ProcessInfo);
+};
+
+bool isEqualSessionId(const Vector<uint8_t> &sessionId1, const Vector<uint8_t> &sessionId2) {
+    if (sessionId1.size() != sessionId2.size()) {
+        return false;
+    }
+    for (size_t i = 0; i < sessionId1.size(); ++i) {
+        if (sessionId1[i] != sessionId2[i]) {
+            return false;
+        }
+    }
+    return true;
+}
+
+DrmSessionManager::DrmSessionManager()
+    : mProcessInfo(new ProcessInfo()),
+      mTime(0) {}
+
+DrmSessionManager::DrmSessionManager(sp<ProcessInfoInterface> processInfo)
+    : mProcessInfo(processInfo),
+      mTime(0) {}
+
+DrmSessionManager::~DrmSessionManager() {}
+
+void DrmSessionManager::addSession(
+        int pid, sp<DrmSessionClientInterface> drm, const Vector<uint8_t> &sessionId) {
+    ALOGV("addSession(pid %d, drm %p, sessionId %s)", pid, drm.get(),
+            GetSessionIdString(sessionId).string());
+
+    Mutex::Autolock lock(mLock);
+    SessionInfo info;
+    info.drm = drm;
+    info.sessionId = sessionId;
+    info.timeStamp = getTime_l();
+    ssize_t index = mSessionMap.indexOfKey(pid);
+    if (index < 0) {
+        // new pid
+        SessionInfos infosForPid;
+        infosForPid.push_back(info);
+        mSessionMap.add(pid, infosForPid);
+    } else {
+        mSessionMap.editValueAt(index).push_back(info);
+    }
+}
+
+void DrmSessionManager::useSession(const Vector<uint8_t> &sessionId) {
+    ALOGV("useSession(%s)", GetSessionIdString(sessionId).string());
+
+    Mutex::Autolock lock(mLock);
+    for (size_t i = 0; i < mSessionMap.size(); ++i) {
+        SessionInfos& infos = mSessionMap.editValueAt(i);
+        for (size_t j = 0; j < infos.size(); ++j) {
+            SessionInfo& info = infos.editItemAt(j);
+            if (isEqualSessionId(sessionId, info.sessionId)) {
+                info.timeStamp = getTime_l();
+                return;
+            }
+        }
+    }
+}
+
+void DrmSessionManager::removeSession(const Vector<uint8_t> &sessionId) {
+    ALOGV("removeSession(%s)", GetSessionIdString(sessionId).string());
+
+    Mutex::Autolock lock(mLock);
+    for (size_t i = 0; i < mSessionMap.size(); ++i) {
+        SessionInfos& infos = mSessionMap.editValueAt(i);
+        for (size_t j = 0; j < infos.size(); ++j) {
+            if (isEqualSessionId(sessionId, infos[j].sessionId)) {
+                infos.removeAt(j);
+                return;
+            }
+        }
+    }
+}
+
+void DrmSessionManager::removeDrm(sp<DrmSessionClientInterface> drm) {
+    ALOGV("removeDrm(%p)", drm.get());
+
+    Mutex::Autolock lock(mLock);
+    bool found = false;
+    for (size_t i = 0; i < mSessionMap.size(); ++i) {
+        SessionInfos& infos = mSessionMap.editValueAt(i);
+        for (size_t j = 0; j < infos.size();) {
+            if (infos[j].drm == drm) {
+                ALOGV("removed session (%s)", GetSessionIdString(infos[j].sessionId).string());
+                j = infos.removeAt(j);
+                found = true;
+            } else {
+                ++j;
+            }
+        }
+        if (found) {
+            break;
+        }
+    }
+}
+
+bool DrmSessionManager::reclaimSession(int callingPid) {
+    ALOGV("reclaimSession(%d)", callingPid);
+
+    sp<DrmSessionClientInterface> drm;
+    Vector<uint8_t> sessionId;
+    {
+        Mutex::Autolock lock(mLock);
+        int callingPriority = mProcessInfo->getPriority(callingPid);
+        int lowestPriorityPid;
+        int lowestPriority;
+        if (!getLowestPriority_l(&lowestPriorityPid, &lowestPriority)) {
+            return false;
+        }
+        if (lowestPriority >= callingPriority) {
+            return false;
+        }
+
+        if (!getLeastUsedSession_l(lowestPriorityPid, &drm, &sessionId)) {
+            return false;
+        }
+    }
+
+    if (drm == NULL) {
+        return false;
+    }
+
+    return drm->reclaimSession(sessionId);
+}
+
+int64_t DrmSessionManager::getTime_l() {
+    return mTime++;
+}
+
+bool DrmSessionManager::getLowestPriority_l(int* lowestPriorityPid, int* lowestPriority) {
+    int pid = -1;
+    int priority = INT_MAX;
+    for (size_t i = 0; i < mSessionMap.size(); ++i) {
+        if (mSessionMap.valueAt(i).size() == 0) {
+            // no opened session by this process.
+            continue;
+        }
+        int tempPid = mSessionMap.keyAt(i);
+        int tempPriority = mProcessInfo->getPriority(tempPid);
+        if (pid == -1) {
+            pid = tempPid;
+            priority = tempPriority;
+        } else {
+            if (tempPriority < priority) {
+                pid = tempPid;
+                priority = tempPriority;
+            }
+        }
+    }
+    if (pid != -1) {
+        *lowestPriorityPid = pid;
+        *lowestPriority = priority;
+    }
+    return (pid != -1);
+}
+
+bool DrmSessionManager::getLeastUsedSession_l(
+        int pid, sp<DrmSessionClientInterface>* drm, Vector<uint8_t>* sessionId) {
+    ssize_t index = mSessionMap.indexOfKey(pid);
+    if (index < 0) {
+        return false;
+    }
+
+    int leastUsedIndex = -1;
+    int64_t minTs = LLONG_MAX;
+    const SessionInfos& infos = mSessionMap.valueAt(index);
+    for (size_t j = 0; j < infos.size(); ++j) {
+        if (leastUsedIndex == -1) {
+            leastUsedIndex = j;
+            minTs = infos[j].timeStamp;
+        } else {
+            if (infos[j].timeStamp < minTs) {
+                leastUsedIndex = j;
+                minTs = infos[j].timeStamp;
+            }
+        }
+    }
+    if (leastUsedIndex != -1) {
+        *drm = infos[leastUsedIndex].drm;
+        *sessionId = infos[leastUsedIndex].sessionId;
+    }
+    return (leastUsedIndex != -1);
+}
+
+}  // namespace android
diff --git a/media/libmediaplayerservice/DrmSessionManager.h b/media/libmediaplayerservice/DrmSessionManager.h
new file mode 100644
index 0000000..1d0ed43
--- /dev/null
+++ b/media/libmediaplayerservice/DrmSessionManager.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2015 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 DRM_SESSION_MANAGER_H_
+
+#define DRM_SESSION_MANAGER_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/RefBase.h>
+#include <utils/KeyedVector.h>
+#include <utils/threads.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+class DrmSessionManagerTest;
+struct DrmSessionClientInterface;
+struct ProcessInfoInterface;
+
+bool isEqualSessionId(const Vector<uint8_t> &sessionId1, const Vector<uint8_t> &sessionId2);
+
+struct SessionInfo {
+    sp<DrmSessionClientInterface> drm;
+    Vector<uint8_t> sessionId;
+    int64_t timeStamp;
+};
+
+typedef Vector<SessionInfo > SessionInfos;
+typedef KeyedVector<int, SessionInfos > PidSessionInfosMap;
+
+struct DrmSessionManager : public RefBase {
+    DrmSessionManager();
+    DrmSessionManager(sp<ProcessInfoInterface> processInfo);
+
+    void addSession(int pid, sp<DrmSessionClientInterface> drm, const Vector<uint8_t>& sessionId);
+    void useSession(const Vector<uint8_t>& sessionId);
+    void removeSession(const Vector<uint8_t>& sessionId);
+    void removeDrm(sp<DrmSessionClientInterface> drm);
+    bool reclaimSession(int callingPid);
+
+protected:
+    virtual ~DrmSessionManager();
+
+private:
+    friend class DrmSessionManagerTest;
+
+    int64_t getTime_l();
+    bool getLowestPriority_l(int* lowestPriorityPid, int* lowestPriority);
+    bool getLeastUsedSession_l(
+            int pid, sp<DrmSessionClientInterface>* drm, Vector<uint8_t>* sessionId);
+
+    sp<ProcessInfoInterface> mProcessInfo;
+    mutable Mutex mLock;
+    PidSessionInfosMap mSessionMap;
+    int64_t mTime;
+
+    DISALLOW_EVIL_CONSTRUCTORS(DrmSessionManager);
+};
+
+}  // namespace android
+
+#endif  // DRM_SESSION_MANAGER_H_
diff --git a/media/libmediaplayerservice/ProcessInfoInterface.h b/media/libmediaplayerservice/ProcessInfoInterface.h
new file mode 100644
index 0000000..bdcc1da
--- /dev/null
+++ b/media/libmediaplayerservice/ProcessInfoInterface.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2015 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 PROCESS_INFO_INTERFACE_H_
+#define PROCESS_INFO_INTERFACE_H_
+
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct ProcessInfoInterface : public RefBase {
+    virtual int getPriority(int pid) = 0;
+
+protected:
+    virtual ~ProcessInfoInterface() {}
+};
+
+}  // namespace android
+
+#endif  // PROCESS_INFO_INTERFACE_H_
diff --git a/media/libmediaplayerservice/tests/Android.mk b/media/libmediaplayerservice/tests/Android.mk
new file mode 100644
index 0000000..69d4ad1
--- /dev/null
+++ b/media/libmediaplayerservice/tests/Android.mk
@@ -0,0 +1,31 @@
+# Build the unit tests.
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+
+LOCAL_MODULE := DrmSessionManager_test
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := \
+	DrmSessionManager_test.cpp \
+
+LOCAL_SHARED_LIBRARIES := \
+	liblog \
+	libmediaplayerservice \
+	libutils \
+
+LOCAL_C_INCLUDES := \
+	frameworks/av/include \
+	frameworks/av/media/libmediaplayerservice \
+
+include $(BUILD_NATIVE_TEST)
+
+# Include subdirectory makefiles
+# ============================================================
+
+# If we're building with ONE_SHOT_MAKEFILE (mm, mmm), then what the framework
+# team really wants is to build the stuff defined by this makefile.
+ifeq (,$(ONE_SHOT_MAKEFILE))
+include $(call first-makefiles-under,$(LOCAL_PATH))
+endif
diff --git a/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp b/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp
new file mode 100644
index 0000000..a199ee1
--- /dev/null
+++ b/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2015 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 "DrmSessionManager_test"
+#include <utils/Log.h>
+
+#include <gtest/gtest.h>
+
+#include "Drm.h"
+#include "DrmSessionClientInterface.h"
+#include "DrmSessionManager.h"
+#include "ProcessInfoInterface.h"
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace android {
+
+struct FakeProcessInfo : public ProcessInfoInterface {
+    FakeProcessInfo() {}
+    virtual ~FakeProcessInfo() {}
+
+    virtual int getPriority(int pid) { return pid; }
+
+private:
+    DISALLOW_EVIL_CONSTRUCTORS(FakeProcessInfo);
+};
+
+struct FakeDrm : public DrmSessionClientInterface {
+    FakeDrm() {}
+    virtual ~FakeDrm() {}
+
+    virtual bool reclaimSession(const Vector<uint8_t>& sessionId) {
+        mReclaimedSessions.push_back(sessionId);
+        return true;
+    }
+
+    const Vector<Vector<uint8_t> >& reclaimedSessions() const {
+        return mReclaimedSessions;
+    }
+
+private:
+    Vector<Vector<uint8_t> > mReclaimedSessions;
+
+    DISALLOW_EVIL_CONSTRUCTORS(FakeDrm);
+};
+
+static const int kTestPid1 = 10;
+static const int kTestPid2 = 20;
+static const uint8_t kTestSessionId1[] = {1, 2, 3};
+static const uint8_t kTestSessionId2[] = {4, 5, 6, 7, 8};
+static const uint8_t kTestSessionId3[] = {9, 0};
+
+class DrmSessionManagerTest : public ::testing::Test {
+public:
+    DrmSessionManagerTest()
+        : mDrmSessionManager(new DrmSessionManager(new FakeProcessInfo())),
+          mTestDrm1(new FakeDrm()),
+          mTestDrm2(new FakeDrm()) {
+        GetSessionId(kTestSessionId1, ARRAY_SIZE(kTestSessionId1), &mSessionId1);
+        GetSessionId(kTestSessionId2, ARRAY_SIZE(kTestSessionId2), &mSessionId2);
+        GetSessionId(kTestSessionId3, ARRAY_SIZE(kTestSessionId3), &mSessionId3);
+    }
+
+protected:
+    static void GetSessionId(const uint8_t* ids, size_t num, Vector<uint8_t>* sessionId) {
+        for (size_t i = 0; i < num; ++i) {
+            sessionId->push_back(ids[i]);
+        }
+    }
+
+    static void ExpectEqSessionInfo(const SessionInfo& info, sp<DrmSessionClientInterface> drm,
+            const Vector<uint8_t>& sessionId, int64_t timeStamp) {
+        EXPECT_EQ(drm, info.drm);
+        EXPECT_TRUE(isEqualSessionId(sessionId, info.sessionId));
+        EXPECT_EQ(timeStamp, info.timeStamp);
+    }
+
+    void addSession() {
+        mDrmSessionManager->addSession(kTestPid1, mTestDrm1, mSessionId1);
+        mDrmSessionManager->addSession(kTestPid2, mTestDrm2, mSessionId2);
+        mDrmSessionManager->addSession(kTestPid2, mTestDrm2, mSessionId3);
+        const PidSessionInfosMap& map = sessionMap();
+        EXPECT_EQ(2, map.size());
+        ssize_t index1 = map.indexOfKey(kTestPid1);
+        ASSERT_GE(index1, 0);
+        const SessionInfos& infos1 = map[index1];
+        EXPECT_EQ(1, infos1.size());
+        ExpectEqSessionInfo(infos1[0], mTestDrm1, mSessionId1, 0);
+
+        ssize_t index2 = map.indexOfKey(kTestPid2);
+        ASSERT_GE(index2, 0);
+        const SessionInfos& infos2 = map[index2];
+        EXPECT_EQ(2, infos2.size());
+        ExpectEqSessionInfo(infos2[0], mTestDrm2, mSessionId2, 1);
+        ExpectEqSessionInfo(infos2[1], mTestDrm2, mSessionId3, 2);
+    }
+
+    const PidSessionInfosMap& sessionMap() {
+        return mDrmSessionManager->mSessionMap;
+    }
+
+    void testGetLowestPriority() {
+        int pid;
+        int priority;
+        EXPECT_FALSE(mDrmSessionManager->getLowestPriority_l(&pid, &priority));
+
+        addSession();
+        EXPECT_TRUE(mDrmSessionManager->getLowestPriority_l(&pid, &priority));
+
+        EXPECT_EQ(kTestPid1, pid);
+        FakeProcessInfo processInfo;
+        EXPECT_EQ(processInfo.getPriority(kTestPid1), priority);
+    }
+
+    void testGetLeastUsedSession() {
+        sp<DrmSessionClientInterface> drm;
+        Vector<uint8_t> sessionId;
+        EXPECT_FALSE(mDrmSessionManager->getLeastUsedSession_l(kTestPid1, &drm, &sessionId));
+
+        addSession();
+
+        EXPECT_TRUE(mDrmSessionManager->getLeastUsedSession_l(kTestPid1, &drm, &sessionId));
+        EXPECT_EQ(mTestDrm1, drm);
+        EXPECT_TRUE(isEqualSessionId(mSessionId1, sessionId));
+
+        EXPECT_TRUE(mDrmSessionManager->getLeastUsedSession_l(kTestPid2, &drm, &sessionId));
+        EXPECT_EQ(mTestDrm2, drm);
+        EXPECT_TRUE(isEqualSessionId(mSessionId2, sessionId));
+
+        // mSessionId2 is no longer the least used session.
+        mDrmSessionManager->useSession(mSessionId2);
+        EXPECT_TRUE(mDrmSessionManager->getLeastUsedSession_l(kTestPid2, &drm, &sessionId));
+        EXPECT_EQ(mTestDrm2, drm);
+        EXPECT_TRUE(isEqualSessionId(mSessionId3, sessionId));
+    }
+
+    sp<DrmSessionManager> mDrmSessionManager;
+    sp<FakeDrm> mTestDrm1;
+    sp<FakeDrm> mTestDrm2;
+    Vector<uint8_t> mSessionId1;
+    Vector<uint8_t> mSessionId2;
+    Vector<uint8_t> mSessionId3;
+};
+
+TEST_F(DrmSessionManagerTest, addSession) {
+    addSession();
+}
+
+TEST_F(DrmSessionManagerTest, useSession) {
+    addSession();
+
+    mDrmSessionManager->useSession(mSessionId1);
+    mDrmSessionManager->useSession(mSessionId3);
+
+    const PidSessionInfosMap& map = sessionMap();
+    const SessionInfos& infos1 = map.valueFor(kTestPid1);
+    const SessionInfos& infos2 = map.valueFor(kTestPid2);
+    ExpectEqSessionInfo(infos1[0], mTestDrm1, mSessionId1, 3);
+    ExpectEqSessionInfo(infos2[1], mTestDrm2, mSessionId3, 4);
+}
+
+TEST_F(DrmSessionManagerTest, removeSession) {
+    addSession();
+
+    mDrmSessionManager->removeSession(mSessionId2);
+
+    const PidSessionInfosMap& map = sessionMap();
+    EXPECT_EQ(2, map.size());
+    const SessionInfos& infos1 = map.valueFor(kTestPid1);
+    const SessionInfos& infos2 = map.valueFor(kTestPid2);
+    EXPECT_EQ(1, infos1.size());
+    EXPECT_EQ(1, infos2.size());
+    // mSessionId2 has been removed.
+    ExpectEqSessionInfo(infos2[0], mTestDrm2, mSessionId3, 2);
+}
+
+TEST_F(DrmSessionManagerTest, removeDrm) {
+    addSession();
+
+    sp<FakeDrm> drm = new FakeDrm;
+    const uint8_t ids[] = {123};
+    Vector<uint8_t> sessionId;
+    GetSessionId(ids, ARRAY_SIZE(ids), &sessionId);
+    mDrmSessionManager->addSession(kTestPid2, drm, sessionId);
+
+    mDrmSessionManager->removeDrm(mTestDrm2);
+
+    const PidSessionInfosMap& map = sessionMap();
+    const SessionInfos& infos2 = map.valueFor(kTestPid2);
+    EXPECT_EQ(1, infos2.size());
+    // mTestDrm2 has been removed.
+    ExpectEqSessionInfo(infos2[0], drm, sessionId, 3);
+}
+
+TEST_F(DrmSessionManagerTest, reclaimSession) {
+    EXPECT_FALSE(mDrmSessionManager->reclaimSession(kTestPid1));
+    addSession();
+
+    // calling pid priority is too low
+    EXPECT_FALSE(mDrmSessionManager->reclaimSession(5));
+
+    EXPECT_TRUE(mDrmSessionManager->reclaimSession(30));
+    EXPECT_EQ(1, mTestDrm1->reclaimedSessions().size());
+    EXPECT_TRUE(isEqualSessionId(mSessionId1, mTestDrm1->reclaimedSessions()[0]));
+
+    mDrmSessionManager->removeSession(mSessionId1);
+
+    // add a session from a higher priority process.
+    sp<FakeDrm> drm = new FakeDrm;
+    const uint8_t ids[] = {456, 7890, 123};
+    Vector<uint8_t> sessionId;
+    GetSessionId(ids, ARRAY_SIZE(ids), &sessionId);
+    mDrmSessionManager->addSession(30, drm, sessionId);
+
+    EXPECT_TRUE(mDrmSessionManager->reclaimSession(40));
+    EXPECT_EQ(1, mTestDrm2->reclaimedSessions().size());
+    // mSessionId2 is reclaimed.
+    EXPECT_TRUE(isEqualSessionId(mSessionId2, mTestDrm2->reclaimedSessions()[0]));
+}
+
+TEST_F(DrmSessionManagerTest, getLowestPriority) {
+    testGetLowestPriority();
+}
+
+TEST_F(DrmSessionManagerTest, getLeastUsedSession_l) {
+    testGetLeastUsedSession();
+}
+
+} // namespace android