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 <binder/IPCThreadState.h>
22#include <binder/IProcessInfoService.h>
23#include <binder/IServiceManager.h>
24#include <media/DrmSessionManager.h>
25#include <media/DrmSessionClientInterface.h>
26#include <media/stagefright/ProcessInfo.h>
27#include <unistd.h>
28#include <utils/String8.h>
29
30namespace android {
31
32static String8 GetSessionIdString(const Vector<uint8_t> &sessionId) {
33    String8 sessionIdStr;
34    for (size_t i = 0; i < sessionId.size(); ++i) {
35        sessionIdStr.appendFormat("%u ", sessionId[i]);
36    }
37    return sessionIdStr;
38}
39
40bool isEqualSessionId(const Vector<uint8_t> &sessionId1, const Vector<uint8_t> &sessionId2) {
41    if (sessionId1.size() != sessionId2.size()) {
42        return false;
43    }
44    for (size_t i = 0; i < sessionId1.size(); ++i) {
45        if (sessionId1[i] != sessionId2[i]) {
46            return false;
47        }
48    }
49    return true;
50}
51
52sp<DrmSessionManager> DrmSessionManager::Instance() {
53    static sp<DrmSessionManager> drmSessionManager = new DrmSessionManager();
54    return drmSessionManager;
55}
56
57DrmSessionManager::DrmSessionManager()
58    : mProcessInfo(new ProcessInfo()),
59      mTime(0) {}
60
61DrmSessionManager::DrmSessionManager(sp<ProcessInfoInterface> processInfo)
62    : mProcessInfo(processInfo),
63      mTime(0) {}
64
65DrmSessionManager::~DrmSessionManager() {}
66
67void DrmSessionManager::addSession(
68        int pid, const sp<DrmSessionClientInterface>& drm, const Vector<uint8_t> &sessionId) {
69    ALOGV("addSession(pid %d, drm %p, sessionId %s)", pid, drm.get(),
70            GetSessionIdString(sessionId).string());
71
72    Mutex::Autolock lock(mLock);
73    SessionInfo info;
74    info.drm = drm;
75    info.sessionId = sessionId;
76    info.timeStamp = getTime_l();
77    ssize_t index = mSessionMap.indexOfKey(pid);
78    if (index < 0) {
79        // new pid
80        SessionInfos infosForPid;
81        infosForPid.push_back(info);
82        mSessionMap.add(pid, infosForPid);
83    } else {
84        mSessionMap.editValueAt(index).push_back(info);
85    }
86}
87
88void DrmSessionManager::useSession(const Vector<uint8_t> &sessionId) {
89    ALOGV("useSession(%s)", GetSessionIdString(sessionId).string());
90
91    Mutex::Autolock lock(mLock);
92    for (size_t i = 0; i < mSessionMap.size(); ++i) {
93        SessionInfos& infos = mSessionMap.editValueAt(i);
94        for (size_t j = 0; j < infos.size(); ++j) {
95            SessionInfo& info = infos.editItemAt(j);
96            if (isEqualSessionId(sessionId, info.sessionId)) {
97                info.timeStamp = getTime_l();
98                return;
99            }
100        }
101    }
102}
103
104void DrmSessionManager::removeSession(const Vector<uint8_t> &sessionId) {
105    ALOGV("removeSession(%s)", GetSessionIdString(sessionId).string());
106
107    Mutex::Autolock lock(mLock);
108    for (size_t i = 0; i < mSessionMap.size(); ++i) {
109        SessionInfos& infos = mSessionMap.editValueAt(i);
110        for (size_t j = 0; j < infos.size(); ++j) {
111            if (isEqualSessionId(sessionId, infos[j].sessionId)) {
112                infos.removeAt(j);
113                return;
114            }
115        }
116    }
117}
118
119void DrmSessionManager::removeDrm(const sp<DrmSessionClientInterface>& drm) {
120    ALOGV("removeDrm(%p)", drm.get());
121
122    Mutex::Autolock lock(mLock);
123    bool found = false;
124    for (size_t i = 0; i < mSessionMap.size(); ++i) {
125        SessionInfos& infos = mSessionMap.editValueAt(i);
126        for (size_t j = 0; j < infos.size();) {
127            if (infos[j].drm == drm) {
128                ALOGV("removed session (%s)", GetSessionIdString(infos[j].sessionId).string());
129                j = infos.removeAt(j);
130                found = true;
131            } else {
132                ++j;
133            }
134        }
135        if (found) {
136            break;
137        }
138    }
139}
140
141bool DrmSessionManager::reclaimSession(int callingPid) {
142    ALOGV("reclaimSession(%d)", callingPid);
143
144    sp<DrmSessionClientInterface> drm;
145    Vector<uint8_t> sessionId;
146    int lowestPriorityPid;
147    int lowestPriority;
148    {
149        Mutex::Autolock lock(mLock);
150        int callingPriority;
151        if (!mProcessInfo->getPriority(callingPid, &callingPriority)) {
152            return false;
153        }
154        if (!getLowestPriority_l(&lowestPriorityPid, &lowestPriority)) {
155            return false;
156        }
157        if (lowestPriority <= callingPriority) {
158            return false;
159        }
160
161        if (!getLeastUsedSession_l(lowestPriorityPid, &drm, &sessionId)) {
162            return false;
163        }
164    }
165
166    if (drm == NULL) {
167        return false;
168    }
169
170    ALOGV("reclaim session(%s) opened by pid %d",
171            GetSessionIdString(sessionId).string(), lowestPriorityPid);
172
173    return drm->reclaimSession(sessionId);
174}
175
176int64_t DrmSessionManager::getTime_l() {
177    return mTime++;
178}
179
180bool DrmSessionManager::getLowestPriority_l(int* lowestPriorityPid, int* lowestPriority) {
181    int pid = -1;
182    int priority = -1;
183    for (size_t i = 0; i < mSessionMap.size(); ++i) {
184        if (mSessionMap.valueAt(i).size() == 0) {
185            // no opened session by this process.
186            continue;
187        }
188        int tempPid = mSessionMap.keyAt(i);
189        int tempPriority;
190        if (!mProcessInfo->getPriority(tempPid, &tempPriority)) {
191            // shouldn't happen.
192            return false;
193        }
194        if (pid == -1) {
195            pid = tempPid;
196            priority = tempPriority;
197        } else {
198            if (tempPriority > priority) {
199                pid = tempPid;
200                priority = tempPriority;
201            }
202        }
203    }
204    if (pid != -1) {
205        *lowestPriorityPid = pid;
206        *lowestPriority = priority;
207    }
208    return (pid != -1);
209}
210
211bool DrmSessionManager::getLeastUsedSession_l(
212        int pid, sp<DrmSessionClientInterface>* drm, Vector<uint8_t>* sessionId) {
213    ssize_t index = mSessionMap.indexOfKey(pid);
214    if (index < 0) {
215        return false;
216    }
217
218    int leastUsedIndex = -1;
219    int64_t minTs = LLONG_MAX;
220    const SessionInfos& infos = mSessionMap.valueAt(index);
221    for (size_t j = 0; j < infos.size(); ++j) {
222        if (leastUsedIndex == -1) {
223            leastUsedIndex = j;
224            minTs = infos[j].timeStamp;
225        } else {
226            if (infos[j].timeStamp < minTs) {
227                leastUsedIndex = j;
228                minTs = infos[j].timeStamp;
229            }
230        }
231    }
232    if (leastUsedIndex != -1) {
233        *drm = infos[leastUsedIndex].drm;
234        *sessionId = infos[leastUsedIndex].sessionId;
235    }
236    return (leastUsedIndex != -1);
237}
238
239}  // namespace android
240