/* * 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 <binder/IPCThreadState.h> #include <binder/IProcessInfoService.h> #include <binder/IServiceManager.h> #include <media/stagefright/ProcessInfo.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; } 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; } sp<DrmSessionManager> DrmSessionManager::Instance() { static sp<DrmSessionManager> drmSessionManager = new DrmSessionManager(); return drmSessionManager; } 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; int lowestPriorityPid; int lowestPriority; { Mutex::Autolock lock(mLock); int callingPriority; if (!mProcessInfo->getPriority(callingPid, &callingPriority)) { return false; } 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; } ALOGV("reclaim session(%s) opened by pid %d", GetSessionIdString(sessionId).string(), lowestPriorityPid); return drm->reclaimSession(sessionId); } int64_t DrmSessionManager::getTime_l() { return mTime++; } bool DrmSessionManager::getLowestPriority_l(int* lowestPriorityPid, int* lowestPriority) { int pid = -1; 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; if (!mProcessInfo->getPriority(tempPid, &tempPriority)) { // shouldn't happen. return false; } 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