drm: use DrmSessionManager for session resource managing.

Bug: 19265536
Change-Id: Ie35184f99c51e7bd94d7c826ffb8e89040a81905
diff --git a/media/libmediaplayerservice/Drm.cpp b/media/libmediaplayerservice/Drm.cpp
index 73f1a2a..d4f6fab 100644
--- a/media/libmediaplayerservice/Drm.cpp
+++ b/media/libmediaplayerservice/Drm.cpp
@@ -23,6 +23,8 @@
 
 #include "Drm.h"
 
+#include "DrmSessionClientInterface.h"
+#include "DrmSessionManager.h"
 #include <media/drm/DrmAPI.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AString.h>
@@ -33,6 +35,10 @@
 
 namespace android {
 
+static inline int getCallingPid() {
+    return IPCThreadState::self()->getCallingPid();
+}
+
 static bool checkPermission(const char* permissionString) {
 #ifndef HAVE_ANDROID_OS
     return true;
@@ -57,14 +63,41 @@
     return memcmp((void *)lhs.array(), (void *)rhs.array(), rhs.size()) < 0;
 }
 
+struct DrmSessionClient : public DrmSessionClientInterface {
+    DrmSessionClient(Drm* drm) : mDrm(drm) {}
+
+    virtual bool reclaimSession(const Vector<uint8_t>& sessionId) {
+        sp<Drm> drm = mDrm.promote();
+        if (drm == NULL) {
+            return true;
+        }
+        status_t err = drm->closeSession(sessionId);
+        if (err != OK) {
+            return false;
+        }
+        drm->sendEvent(DrmPlugin::kDrmPluginEventSessionReclaimed, 0, &sessionId, NULL);
+        return true;
+    }
+
+protected:
+    virtual ~DrmSessionClient() {}
+
+private:
+    wp<Drm> mDrm;
+
+    DISALLOW_EVIL_CONSTRUCTORS(DrmSessionClient);
+};
+
 Drm::Drm()
     : mInitCheck(NO_INIT),
+      mDrmSessionClient(new DrmSessionClient(this)),
       mListener(NULL),
       mFactory(NULL),
       mPlugin(NULL) {
 }
 
 Drm::~Drm() {
+    DrmSessionManager::Instance()->removeDrm(mDrmSessionClient);
     delete mPlugin;
     mPlugin = NULL;
     closeFactory();
@@ -289,7 +322,18 @@
         return -EINVAL;
     }
 
-    return mPlugin->openSession(sessionId);
+    status_t err = mPlugin->openSession(sessionId);
+    if (err == ERROR_DRM_RESOURCE_BUSY) {
+        bool retry = false;
+        retry = DrmSessionManager::Instance()->reclaimSession(getCallingPid());
+        if (retry) {
+            err = mPlugin->openSession(sessionId);
+        }
+    }
+    if (err == OK) {
+        DrmSessionManager::Instance()->addSession(getCallingPid(), mDrmSessionClient, sessionId);
+    }
+    return err;
 }
 
 status_t Drm::closeSession(Vector<uint8_t> const &sessionId) {
@@ -303,7 +347,11 @@
         return -EINVAL;
     }
 
-    return mPlugin->closeSession(sessionId);
+    status_t err = mPlugin->closeSession(sessionId);
+    if (err == OK) {
+        DrmSessionManager::Instance()->removeSession(sessionId);
+    }
+    return err;
 }
 
 status_t Drm::getKeyRequest(Vector<uint8_t> const &sessionId,
@@ -321,6 +369,8 @@
         return -EINVAL;
     }
 
+    DrmSessionManager::Instance()->useSession(sessionId);
+
     return mPlugin->getKeyRequest(sessionId, initData, mimeType, keyType,
                                   optionalParameters, request, defaultUrl);
 }
@@ -338,6 +388,8 @@
         return -EINVAL;
     }
 
+    DrmSessionManager::Instance()->useSession(sessionId);
+
     return mPlugin->provideKeyResponse(sessionId, response, keySetId);
 }
 
@@ -367,6 +419,8 @@
         return -EINVAL;
     }
 
+    DrmSessionManager::Instance()->useSession(sessionId);
+
     return mPlugin->restoreKeys(sessionId, keySetId);
 }
 
@@ -382,6 +436,8 @@
         return -EINVAL;
     }
 
+    DrmSessionManager::Instance()->useSession(sessionId);
+
     return mPlugin->queryKeyStatus(sessionId, infoMap);
 }
 
@@ -561,6 +617,8 @@
         return -EINVAL;
     }
 
+    DrmSessionManager::Instance()->useSession(sessionId);
+
     return mPlugin->setCipherAlgorithm(sessionId, algorithm);
 }
 
@@ -576,6 +634,8 @@
         return -EINVAL;
     }
 
+    DrmSessionManager::Instance()->useSession(sessionId);
+
     return mPlugin->setMacAlgorithm(sessionId, algorithm);
 }
 
@@ -594,6 +654,8 @@
         return -EINVAL;
     }
 
+    DrmSessionManager::Instance()->useSession(sessionId);
+
     return mPlugin->encrypt(sessionId, keyId, input, iv, output);
 }
 
@@ -612,6 +674,8 @@
         return -EINVAL;
     }
 
+    DrmSessionManager::Instance()->useSession(sessionId);
+
     return mPlugin->decrypt(sessionId, keyId, input, iv, output);
 }
 
@@ -629,6 +693,8 @@
         return -EINVAL;
     }
 
+    DrmSessionManager::Instance()->useSession(sessionId);
+
     return mPlugin->sign(sessionId, keyId, message, signature);
 }
 
@@ -647,6 +713,8 @@
         return -EINVAL;
     }
 
+    DrmSessionManager::Instance()->useSession(sessionId);
+
     return mPlugin->verify(sessionId, keyId, message, signature, match);
 }
 
@@ -669,6 +737,8 @@
         return -EPERM;
     }
 
+    DrmSessionManager::Instance()->useSession(sessionId);
+
     return mPlugin->signRSA(sessionId, algorithm, message, wrappedKey, signature);
 }
 
diff --git a/media/libmediaplayerservice/Drm.h b/media/libmediaplayerservice/Drm.h
index 0e1eb2c..0cea639 100644
--- a/media/libmediaplayerservice/Drm.h
+++ b/media/libmediaplayerservice/Drm.h
@@ -28,6 +28,7 @@
 
 struct DrmFactory;
 struct DrmPlugin;
+struct DrmSessionClientInterface;
 
 struct Drm : public BnDrm,
              public IBinder::DeathRecipient,
@@ -138,6 +139,8 @@
 
     status_t mInitCheck;
 
+    sp<DrmSessionClientInterface> mDrmSessionClient;
+
     sp<IDrmClient> mListener;
     mutable Mutex mEventLock;
     mutable Mutex mNotifyLock;
diff --git a/media/libmediaplayerservice/DrmSessionManager.cpp b/media/libmediaplayerservice/DrmSessionManager.cpp
index 43346e0..6e17eb1 100644
--- a/media/libmediaplayerservice/DrmSessionManager.cpp
+++ b/media/libmediaplayerservice/DrmSessionManager.cpp
@@ -23,6 +23,8 @@
 #include "DrmSessionClientInterface.h"
 #include "ProcessInfoInterface.h"
 #include <binder/IPCThreadState.h>
+#include <binder/IProcessInfoService.h>
+#include <binder/IServiceManager.h>
 #include <unistd.h>
 #include <utils/String8.h>
 
@@ -39,12 +41,25 @@
 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;
+    virtual bool getPriority(int pid, int* priority) {
+        sp<IBinder> binder = defaultServiceManager()->getService(String16("processinfo"));
+        sp<IProcessInfoService> service = interface_cast<IProcessInfoService>(binder);
+
+        size_t length = 1;
+        int32_t states;
+        status_t err = service->getProcessStatesFromPids(length, &pid, &states);
+        if (err != OK) {
+            ALOGE("getProcessStatesFromPids failed");
+            return false;
+        }
+        ALOGV("pid %d states %d", pid, states);
+        if (states < 0) {
+            return false;
+        }
+
+        // Use process state as the priority. Lower the value, higher the priority.
+        *priority = states;
+        return true;
     }
 
 protected:
@@ -66,6 +81,11 @@
     return true;
 }
 
+sp<DrmSessionManager> DrmSessionManager::Instance() {
+    static sp<DrmSessionManager> drmSessionManager = new DrmSessionManager();
+    return drmSessionManager;
+}
+
 DrmSessionManager::DrmSessionManager()
     : mProcessInfo(new ProcessInfo()),
       mTime(0) {}
@@ -155,15 +175,18 @@
 
     sp<DrmSessionClientInterface> drm;
     Vector<uint8_t> sessionId;
+    int lowestPriorityPid;
+    int lowestPriority;
     {
         Mutex::Autolock lock(mLock);
-        int callingPriority = mProcessInfo->getPriority(callingPid);
-        int lowestPriorityPid;
-        int lowestPriority;
+        int callingPriority;
+        if (!mProcessInfo->getPriority(callingPid, &callingPriority)) {
+            return false;
+        }
         if (!getLowestPriority_l(&lowestPriorityPid, &lowestPriority)) {
             return false;
         }
-        if (lowestPriority >= callingPriority) {
+        if (lowestPriority <= callingPriority) {
             return false;
         }
 
@@ -176,6 +199,9 @@
         return false;
     }
 
+    ALOGV("reclaim session(%s) opened by pid %d",
+            GetSessionIdString(sessionId).string(), lowestPriorityPid);
+
     return drm->reclaimSession(sessionId);
 }
 
@@ -185,19 +211,23 @@
 
 bool DrmSessionManager::getLowestPriority_l(int* lowestPriorityPid, int* lowestPriority) {
     int pid = -1;
-    int priority = INT_MAX;
+    int priority = -1;
     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);
+        int tempPriority;
+        if (!mProcessInfo->getPriority(tempPid, &tempPriority)) {
+            // shouldn't happen.
+            return false;
+        }
         if (pid == -1) {
             pid = tempPid;
             priority = tempPriority;
         } else {
-            if (tempPriority < priority) {
+            if (tempPriority > priority) {
                 pid = tempPid;
                 priority = tempPriority;
             }
diff --git a/media/libmediaplayerservice/DrmSessionManager.h b/media/libmediaplayerservice/DrmSessionManager.h
index 1d0ed43..ba5c268 100644
--- a/media/libmediaplayerservice/DrmSessionManager.h
+++ b/media/libmediaplayerservice/DrmSessionManager.h
@@ -42,6 +42,8 @@
 typedef KeyedVector<int, SessionInfos > PidSessionInfosMap;
 
 struct DrmSessionManager : public RefBase {
+    static sp<DrmSessionManager> Instance();
+
     DrmSessionManager();
     DrmSessionManager(sp<ProcessInfoInterface> processInfo);
 
diff --git a/media/libmediaplayerservice/ProcessInfoInterface.h b/media/libmediaplayerservice/ProcessInfoInterface.h
index bdcc1da..222f92d 100644
--- a/media/libmediaplayerservice/ProcessInfoInterface.h
+++ b/media/libmediaplayerservice/ProcessInfoInterface.h
@@ -22,7 +22,7 @@
 namespace android {
 
 struct ProcessInfoInterface : public RefBase {
-    virtual int getPriority(int pid) = 0;
+    virtual bool getPriority(int pid, int* priority) = 0;
 
 protected:
     virtual ~ProcessInfoInterface() {}
diff --git a/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp b/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp
index a199ee1..782c1a5 100644
--- a/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp
+++ b/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp
@@ -32,7 +32,12 @@
     FakeProcessInfo() {}
     virtual ~FakeProcessInfo() {}
 
-    virtual int getPriority(int pid) { return pid; }
+    virtual bool getPriority(int pid, int* priority) {
+        // For testing, use pid as priority.
+        // Lower the value higher the priority.
+        *priority = pid;
+        return true;
+    }
 
 private:
     DISALLOW_EVIL_CONSTRUCTORS(FakeProcessInfo);
@@ -57,7 +62,7 @@
     DISALLOW_EVIL_CONSTRUCTORS(FakeDrm);
 };
 
-static const int kTestPid1 = 10;
+static const int kTestPid1 = 30;
 static const int kTestPid2 = 20;
 static const uint8_t kTestSessionId1[] = {1, 2, 3};
 static const uint8_t kTestSessionId2[] = {4, 5, 6, 7, 8};
@@ -122,7 +127,9 @@
 
         EXPECT_EQ(kTestPid1, pid);
         FakeProcessInfo processInfo;
-        EXPECT_EQ(processInfo.getPriority(kTestPid1), priority);
+        int priority1;
+        processInfo.getPriority(kTestPid1, &priority1);
+        EXPECT_EQ(priority1, priority);
     }
 
     void testGetLeastUsedSession() {
@@ -210,9 +217,9 @@
     addSession();
 
     // calling pid priority is too low
-    EXPECT_FALSE(mDrmSessionManager->reclaimSession(5));
+    EXPECT_FALSE(mDrmSessionManager->reclaimSession(50));
 
-    EXPECT_TRUE(mDrmSessionManager->reclaimSession(30));
+    EXPECT_TRUE(mDrmSessionManager->reclaimSession(10));
     EXPECT_EQ(1, mTestDrm1->reclaimedSessions().size());
     EXPECT_TRUE(isEqualSessionId(mSessionId1, mTestDrm1->reclaimedSessions()[0]));
 
@@ -223,9 +230,9 @@
     const uint8_t ids[] = {456, 7890, 123};
     Vector<uint8_t> sessionId;
     GetSessionId(ids, ARRAY_SIZE(ids), &sessionId);
-    mDrmSessionManager->addSession(30, drm, sessionId);
+    mDrmSessionManager->addSession(15, drm, sessionId);
 
-    EXPECT_TRUE(mDrmSessionManager->reclaimSession(40));
+    EXPECT_TRUE(mDrmSessionManager->reclaimSession(18));
     EXPECT_EQ(1, mTestDrm2->reclaimedSessions().size());
     // mSessionId2 is reclaimed.
     EXPECT_TRUE(isEqualSessionId(mSessionId2, mTestDrm2->reclaimedSessions()[0]));