1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//#define LOG_NDEBUG 0
18#define LOG_TAG "DrmSessionManager"
19#include <utils/Log.h>
20
21#include "DrmSessionManager.h"
22
23#include "DrmSessionClientInterface.h"
24#include <binder/IPCThreadState.h>
25#include <binder/IProcessInfoService.h>
26#include <binder/IServiceManager.h>
27#include <media/stagefright/ProcessInfo.h>
28#include <unistd.h>
29#include <utils/String8.h>
30
31namespace android {
32
33static String8 GetSessionIdString(const Vector<uint8_t> &sessionId) {
34    String8 sessionIdStr;
35    for (size_t i = 0; i < sessionId.size(); ++i) {
36        sessionIdStr.appendFormat("%u ", sessionId[i]);
37    }
38    return sessionIdStr;
39}
40
41bool isEqualSessionId(const Vector<uint8_t> &sessionId1, const Vector<uint8_t> &sessionId2) {
42    if (sessionId1.size() != sessionId2.size()) {
43        return false;
44    }
45    for (size_t i = 0; i < sessionId1.size(); ++i) {
46        if (sessionId1[i] != sessionId2[i]) {
47            return false;
48        }
49    }
50    return true;
51}
52
53sp<DrmSessionManager> DrmSessionManager::Instance() {
54    static sp<DrmSessionManager> drmSessionManager = new DrmSessionManager();
55    return drmSessionManager;
56}
57
58DrmSessionManager::DrmSessionManager()
59    : mProcessInfo(new ProcessInfo()),
60      mTime(0) {}
61
62DrmSessionManager::DrmSessionManager(sp<ProcessInfoInterface> processInfo)
63    : mProcessInfo(processInfo),
64      mTime(0) {}
65
66DrmSessionManager::~DrmSessionManager() {}
67
68void DrmSessionManager::addSession(
69        int pid, sp<DrmSessionClientInterface> drm, const Vector<uint8_t> &sessionId) {
70    ALOGV("addSession(pid %d, drm %p, sessionId %s)", pid, drm.get(),
71            GetSessionIdString(sessionId).string());
72
73    Mutex::Autolock lock(mLock);
74    SessionInfo info;
75    info.drm = drm;
76    info.sessionId = sessionId;
77    info.timeStamp = getTime_l();
78    ssize_t index = mSessionMap.indexOfKey(pid);
79    if (index < 0) {
80        // new pid
81        SessionInfos infosForPid;
82        infosForPid.push_back(info);
83        mSessionMap.add(pid, infosForPid);
84    } else {
85        mSessionMap.editValueAt(index).push_back(info);
86    }
87}
88
89void DrmSessionManager::useSession(const Vector<uint8_t> &sessionId) {
90    ALOGV("useSession(%s)", GetSessionIdString(sessionId).string());
91
92    Mutex::Autolock lock(mLock);
93    for (size_t i = 0; i < mSessionMap.size(); ++i) {
94        SessionInfos& infos = mSessionMap.editValueAt(i);
95        for (size_t j = 0; j < infos.size(); ++j) {
96            SessionInfo& info = infos.editItemAt(j);
97            if (isEqualSessionId(sessionId, info.sessionId)) {
98                info.timeStamp = getTime_l();
99                return;
100            }
101        }
102    }
103}
104
105void DrmSessionManager::removeSession(const Vector<uint8_t> &sessionId) {
106    ALOGV("removeSession(%s)", GetSessionIdString(sessionId).string());
107
108    Mutex::Autolock lock(mLock);
109    for (size_t i = 0; i < mSessionMap.size(); ++i) {
110        SessionInfos& infos = mSessionMap.editValueAt(i);
111        for (size_t j = 0; j < infos.size(); ++j) {
112            if (isEqualSessionId(sessionId, infos[j].sessionId)) {
113                infos.removeAt(j);
114                return;
115            }
116        }
117    }
118}
119
120void DrmSessionManager::removeDrm(sp<DrmSessionClientInterface> drm) {
121    ALOGV("removeDrm(%p)", drm.get());
122
123    Mutex::Autolock lock(mLock);
124    bool found = false;
125    for (size_t i = 0; i < mSessionMap.size(); ++i) {
126        SessionInfos& infos = mSessionMap.editValueAt(i);
127        for (size_t j = 0; j < infos.size();) {
128            if (infos[j].drm == drm) {
129                ALOGV("removed session (%s)", GetSessionIdString(infos[j].sessionId).string());
130                j = infos.removeAt(j);
131                found = true;
132            } else {
133                ++j;
134            }
135        }
136        if (found) {
137            break;
138        }
139    }
140}
141
142bool DrmSessionManager::reclaimSession(int callingPid) {
143    ALOGV("reclaimSession(%d)", callingPid);
144
145    sp<DrmSessionClientInterface> drm;
146    Vector<uint8_t> sessionId;
147    int lowestPriorityPid;
148    int lowestPriority;
149    {
150        Mutex::Autolock lock(mLock);
151        int callingPriority;
152        if (!mProcessInfo->getPriority(callingPid, &callingPriority)) {
153            return false;
154        }
155        if (!getLowestPriority_l(&lowestPriorityPid, &lowestPriority)) {
156            return false;
157        }
158        if (lowestPriority <= callingPriority) {
159            return false;
160        }
161
162        if (!getLeastUsedSession_l(lowestPriorityPid, &drm, &sessionId)) {
163            return false;
164        }
165    }
166
167    if (drm == NULL) {
168        return false;
169    }
170
171    ALOGV("reclaim session(%s) opened by pid %d",
172            GetSessionIdString(sessionId).string(), lowestPriorityPid);
173
174    return drm->reclaimSession(sessionId);
175}
176
177int64_t DrmSessionManager::getTime_l() {
178    return mTime++;
179}
180
181bool DrmSessionManager::getLowestPriority_l(int* lowestPriorityPid, int* lowestPriority) {
182    int pid = -1;
183    int priority = -1;
184    for (size_t i = 0; i < mSessionMap.size(); ++i) {
185        if (mSessionMap.valueAt(i).size() == 0) {
186            // no opened session by this process.
187            continue;
188        }
189        int tempPid = mSessionMap.keyAt(i);
190        int tempPriority;
191        if (!mProcessInfo->getPriority(tempPid, &tempPriority)) {
192            // shouldn't happen.
193            return false;
194        }
195        if (pid == -1) {
196            pid = tempPid;
197            priority = tempPriority;
198        } else {
199            if (tempPriority > priority) {
200                pid = tempPid;
201                priority = tempPriority;
202            }
203        }
204    }
205    if (pid != -1) {
206        *lowestPriorityPid = pid;
207        *lowestPriority = priority;
208    }
209    return (pid != -1);
210}
211
212bool DrmSessionManager::getLeastUsedSession_l(
213        int pid, sp<DrmSessionClientInterface>* drm, Vector<uint8_t>* sessionId) {
214    ssize_t index = mSessionMap.indexOfKey(pid);
215    if (index < 0) {
216        return false;
217    }
218
219    int leastUsedIndex = -1;
220    int64_t minTs = LLONG_MAX;
221    const SessionInfos& infos = mSessionMap.valueAt(index);
222    for (size_t j = 0; j < infos.size(); ++j) {
223        if (leastUsedIndex == -1) {
224            leastUsedIndex = j;
225            minTs = infos[j].timeStamp;
226        } else {
227            if (infos[j].timeStamp < minTs) {
228                leastUsedIndex = j;
229                minTs = infos[j].timeStamp;
230            }
231        }
232    }
233    if (leastUsedIndex != -1) {
234        *drm = infos[leastUsedIndex].drm;
235        *sessionId = infos[leastUsedIndex].sessionId;
236    }
237    return (leastUsedIndex != -1);
238}
239
240}  // namespace android
241