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 <aidl/android/media/IResourceManagerClient.h>
22 #include <aidl/android/media/IResourceManagerService.h>
23 #include <aidl/android/media/MediaResourceParcel.h>
24 #include <android/binder_ibinder.h>
25 #include <android/binder_manager.h>
26 #include <cutils/properties.h>
27 #include <mediadrm/DrmUtils.h>
28 #include <mediadrm/DrmSessionManager.h>
29 #include <unistd.h>
30 #include <utils/String8.h>
31
32 #include <vector>
33
34 namespace android {
35
36 using aidl::android::media::MediaResourceParcel;
37 using aidl::android::media::ClientInfoParcel;
38
39 using ::ndk::ScopedAStatus;
40
GetSessionIdString(const Vector<uint8_t> & sessionId)41 static String8 GetSessionIdString(const Vector<uint8_t> &sessionId) {
42 String8 sessionIdStr;
43 for (size_t i = 0; i < sessionId.size(); ++i) {
44 sessionIdStr.appendFormat("%u ", sessionId[i]);
45 }
46 return sessionIdStr;
47 }
48
49 template <typename Byte = uint8_t>
toStdVec(const Vector<uint8_t> & vector)50 static std::vector<Byte> toStdVec(const Vector<uint8_t> &vector) {
51 auto v = reinterpret_cast<const Byte *>(vector.array());
52 std::vector<Byte> vec(v, v + vector.size());
53 return vec;
54 }
55
toAndroidVec(const std::vector<uint8_t> & array)56 static Vector<uint8_t> toAndroidVec(const std::vector<uint8_t>& array) {
57 Vector<uint8_t> vec;
58 vec.appendArray(array.data(), array.size());
59 return vec;
60 }
61
toResourceVec(const Vector<uint8_t> & sessionId,int64_t value)62 static std::vector<MediaResourceParcel> toResourceVec(
63 const Vector<uint8_t> &sessionId, int64_t value) {
64 using Type = aidl::android::media::MediaResourceType;
65 using SubType = aidl::android::media::MediaResourceSubType;
66 std::vector<MediaResourceParcel> resources;
67 MediaResourceParcel resource{
68 Type::kDrmSession, SubType::kUnspecifiedSubType,
69 toStdVec<>(sessionId), value};
70 resources.push_back(resource);
71 return resources;
72 }
73
isEqualSessionId(const Vector<uint8_t> & sessionId1,const Vector<uint8_t> & sessionId2)74 bool isEqualSessionId(const Vector<uint8_t> &sessionId1, const Vector<uint8_t> &sessionId2) {
75 if (sessionId1.size() != sessionId2.size()) {
76 return false;
77 }
78 for (size_t i = 0; i < sessionId1.size(); ++i) {
79 if (sessionId1[i] != sessionId2[i]) {
80 return false;
81 }
82 }
83 return true;
84 }
85
Instance()86 sp<DrmSessionManager> DrmSessionManager::Instance() {
87 static sp<DrmSessionManager> drmSessionManager = new DrmSessionManager();
88 drmSessionManager->init();
89 return drmSessionManager;
90 }
91
DrmSessionManager()92 DrmSessionManager::DrmSessionManager()
93 : DrmSessionManager(nullptr) {
94 }
95
DrmSessionManager(const std::shared_ptr<IResourceManagerService> & service)96 DrmSessionManager::DrmSessionManager(const std::shared_ptr<IResourceManagerService> &service)
97 : mService(service),
98 mDeathRecipient(::ndk::ScopedAIBinder_DeathRecipient(
99 AIBinder_DeathRecipient_new(ResourceManagerServiceDied))) {
100 // Setting callback notification when DeathRecipient gets deleted.
101 AIBinder_DeathRecipient_setOnUnlinked(mDeathRecipient.get(), BinderUnlinkedCallback);
102 }
103
~DrmSessionManager()104 DrmSessionManager::~DrmSessionManager() {
105 if (mService != NULL) {
106 AIBinder_unlinkToDeath(mService->asBinder().get(), mDeathRecipient.get(), this);
107 }
108 }
109
init()110 status_t DrmSessionManager::init() {
111 Mutex::Autolock lock(mLock);
112 getResourceManagerService_l();
113 if (mService == nullptr) {
114 ALOGE("Failed to init ResourceManagerService");
115 return DEAD_OBJECT;
116 }
117
118 return OK;
119 }
120
getResourceManagerService_l()121 void DrmSessionManager::getResourceManagerService_l() {
122 if (mService != nullptr) {
123 return;
124 }
125
126 // Get binder interface to resource manager.
127 ::ndk::SpAIBinder binder(AServiceManager_waitForService("media.resource_manager"));
128 mService = IResourceManagerService::fromBinder(binder);
129 if (mService == nullptr) {
130 ALOGE("Failed to get ResourceManagerService");
131 return;
132 }
133
134 // Create the context that is passed as cookie to the binder death notification.
135 // The context gets deleted at BinderUnlinkedCallback.
136 BinderDiedContext* context = new BinderDiedContext{
137 .mDrmSessionManager = wp<DrmSessionManager>::fromExisting(this)};
138 // Register for the callbacks by linking to death notification.
139 AIBinder_linkToDeath(mService->asBinder().get(), mDeathRecipient.get(), context);
140
141 // If the RM was restarted, re-register all the resources.
142 if (mBinderDied) {
143 reRegisterAllResources_l();
144 mBinderDied = false;
145 }
146 }
147
reRegisterAllResources_l()148 void DrmSessionManager::reRegisterAllResources_l() {
149 if (mSessionMap.empty()) {
150 // Nothing to register.
151 ALOGV("No resources to add");
152 return;
153 }
154
155 if (mService == nullptr) {
156 ALOGW("Service isn't available");
157 return;
158 }
159
160 // Go through the session map and re-register all the resources for those sessions.
161 for (SessionInfoMap::const_iterator iter = mSessionMap.begin();
162 iter != mSessionMap.end(); ++iter) {
163 ClientInfoParcel clientInfo{.pid = static_cast<int32_t>(iter->second.pid),
164 .uid = static_cast<int32_t>(iter->second.uid),
165 .id = iter->second.clientId};
166 mService->addResource(clientInfo, iter->second.drm,
167 toResourceVec(toAndroidVec(iter->first), iter->second.resourceValue));
168 }
169 }
170
addSession(int pid,const std::shared_ptr<IResourceManagerClient> & drm,const Vector<uint8_t> & sessionId)171 void DrmSessionManager::addSession(int pid,
172 const std::shared_ptr<IResourceManagerClient>& drm, const Vector<uint8_t> &sessionId) {
173 uid_t uid = AIBinder_getCallingUid();
174 ALOGV("addSession(pid %d, uid %d, drm %p, sessionId %s)", pid, uid, drm.get(),
175 GetSessionIdString(sessionId).c_str());
176
177 Mutex::Autolock lock(mLock);
178 if (mService == NULL) {
179 return;
180 }
181
182 static int64_t clientId = 0;
183 mSessionMap[toStdVec(sessionId)] = (SessionInfo){pid, uid, clientId, drm, INT64_MAX};
184 ClientInfoParcel clientInfo{.pid = static_cast<int32_t>(pid),
185 .uid = static_cast<int32_t>(uid),
186 .id = clientId++};
187 mService->addResource(clientInfo, drm, toResourceVec(sessionId, INT64_MAX));
188 }
189
useSession(const Vector<uint8_t> & sessionId)190 void DrmSessionManager::useSession(const Vector<uint8_t> &sessionId) {
191 ALOGV("useSession(%s)", GetSessionIdString(sessionId).c_str());
192
193 Mutex::Autolock lock(mLock);
194 auto it = mSessionMap.find(toStdVec(sessionId));
195 if (mService == NULL || it == mSessionMap.end()) {
196 return;
197 }
198
199 auto info = it->second;
200 info.resourceValue = -1;
201 ClientInfoParcel clientInfo{.pid = static_cast<int32_t>(info.pid),
202 .uid = static_cast<int32_t>(info.uid),
203 .id = info.clientId};
204 mService->addResource(clientInfo, NULL, toResourceVec(sessionId, -1));
205 }
206
removeSession(const Vector<uint8_t> & sessionId)207 void DrmSessionManager::removeSession(const Vector<uint8_t> &sessionId) {
208 ALOGV("removeSession(%s)", GetSessionIdString(sessionId).c_str());
209
210 Mutex::Autolock lock(mLock);
211 auto it = mSessionMap.find(toStdVec(sessionId));
212 if (mService == NULL || it == mSessionMap.end()) {
213 return;
214 }
215
216 auto info = it->second;
217 // removeClient instead of removeSession because each client has only one session
218 ClientInfoParcel clientInfo{.pid = static_cast<int32_t>(info.pid),
219 .uid = static_cast<int32_t>(info.uid),
220 .id = info.clientId};
221 mService->removeClient(clientInfo);
222 mSessionMap.erase(it);
223 }
224
reclaimSession(int callingPid)225 bool DrmSessionManager::reclaimSession(int callingPid) {
226 ALOGV("reclaimSession(%d)", callingPid);
227
228 // unlock early because reclaimResource might callback into removeSession
229 mLock.lock();
230 std::shared_ptr<IResourceManagerService> service(mService);
231 mLock.unlock();
232
233 if (service == NULL) {
234 return false;
235 }
236
237 // cannot update mSessionMap because we do not know which sessionId is reclaimed;
238 // we rely on IResourceManagerClient to removeSession in reclaimResource
239 Vector<uint8_t> placeHolder;
240 bool success;
241 uid_t uid = AIBinder_getCallingUid();
242 ClientInfoParcel clientInfo{.pid = static_cast<int32_t>(callingPid),
243 .uid = static_cast<int32_t>(uid)};
244 ScopedAStatus status = service->reclaimResource(
245 clientInfo, toResourceVec(placeHolder, INT64_MAX), &success);
246 return status.isOk() && success;
247 }
248
getSessionCount() const249 size_t DrmSessionManager::getSessionCount() const {
250 Mutex::Autolock lock(mLock);
251 return mSessionMap.size();
252 }
253
containsSession(const Vector<uint8_t> & sessionId) const254 bool DrmSessionManager::containsSession(const Vector<uint8_t>& sessionId) const {
255 Mutex::Autolock lock(mLock);
256 return mSessionMap.count(toStdVec(sessionId));
257 }
258
binderDied()259 void DrmSessionManager::binderDied() {
260 ALOGW("ResourceManagerService died.");
261 Mutex::Autolock lock(mLock);
262 mService = nullptr;
263 mBinderDied = true;
264 // start an async operation that will reconnect with the RM and
265 // re-registers all the resources.
266 mGetServiceFuture = std::async(std::launch::async, [this] { getResourceManagerService(); });
267 }
268
ResourceManagerServiceDied(void * cookie)269 void DrmSessionManager::ResourceManagerServiceDied(void* cookie) {
270 BinderDiedContext* context = reinterpret_cast<BinderDiedContext*>(cookie);
271
272 // Validate the context and check if the DrmSessionManager object is still in scope.
273 if (context != nullptr) {
274 sp<DrmSessionManager> thiz = context->mDrmSessionManager.promote();
275 if (thiz != nullptr) {
276 thiz->binderDied();
277 } else {
278 ALOGI("DrmSessionManager is out of scope already");
279 }
280 }
281 }
282
BinderUnlinkedCallback(void * cookie)283 void DrmSessionManager::BinderUnlinkedCallback(void* cookie) {
284 BinderDiedContext* context = reinterpret_cast<BinderDiedContext*>(cookie);
285 // Since we don't need the context anymore, we are deleting it now.
286 delete context;
287 }
288
289 } // namespace android
290