1 /*
2 **
3 ** Copyright 2015, The Android Open Source Project
4 **
5 ** Licensed under the Apache License, Version 2.0 (the "License");
6 ** you may not use this file except in compliance with the License.
7 ** You may obtain a copy of the License at
8 **
9 **     http://www.apache.org/licenses/LICENSE-2.0
10 **
11 ** Unless required by applicable law or agreed to in writing, software
12 ** distributed under the License is distributed on an "AS IS" BASIS,
13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 ** See the License for the specific language governing permissions and
15 ** limitations under the License.
16 */
17 
18 //#define LOG_NDEBUG 0
19 #define LOG_TAG "ResourceManagerService"
20 #include <utils/Log.h>
21 
22 #include <binder/IMediaResourceMonitor.h>
23 #include <binder/IServiceManager.h>
24 #include <dirent.h>
25 #include <media/stagefright/ProcessInfo.h>
26 #include <string.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <sys/time.h>
30 #include <unistd.h>
31 
32 #include "ResourceManagerService.h"
33 #include "ServiceLog.h"
34 
35 namespace android {
36 
37 namespace {
38 
39 class DeathNotifier : public IBinder::DeathRecipient {
40 public:
DeathNotifier(const wp<ResourceManagerService> & service,int pid,int64_t clientId)41     DeathNotifier(const wp<ResourceManagerService> &service, int pid, int64_t clientId)
42         : mService(service), mPid(pid), mClientId(clientId) {}
43 
binderDied(const wp<IBinder> &)44     virtual void binderDied(const wp<IBinder> & /* who */) override {
45         // Don't check for pid validity since we know it's already dead.
46         sp<ResourceManagerService> service = mService.promote();
47         if (service == nullptr) {
48             ALOGW("ResourceManagerService is dead as well.");
49             return;
50         }
51         service->removeResource(mPid, mClientId, false);
52     }
53 
54 private:
55     wp<ResourceManagerService> mService;
56     int mPid;
57     int64_t mClientId;
58 };
59 
60 }  // namespace
61 
62 template <typename T>
getString(const Vector<T> & items)63 static String8 getString(const Vector<T> &items) {
64     String8 itemsStr;
65     for (size_t i = 0; i < items.size(); ++i) {
66         itemsStr.appendFormat("%s ", items[i].toString().string());
67     }
68     return itemsStr;
69 }
70 
hasResourceType(MediaResource::Type type,const Vector<MediaResource> & resources)71 static bool hasResourceType(MediaResource::Type type, const Vector<MediaResource>& resources) {
72     for (size_t i = 0; i < resources.size(); ++i) {
73         if (resources[i].mType == type) {
74             return true;
75         }
76     }
77     return false;
78 }
79 
hasResourceType(MediaResource::Type type,const ResourceInfos & infos)80 static bool hasResourceType(MediaResource::Type type, const ResourceInfos& infos) {
81     for (size_t i = 0; i < infos.size(); ++i) {
82         if (hasResourceType(type, infos[i].resources)) {
83             return true;
84         }
85     }
86     return false;
87 }
88 
getResourceInfosForEdit(int pid,PidResourceInfosMap & map)89 static ResourceInfos& getResourceInfosForEdit(
90         int pid,
91         PidResourceInfosMap& map) {
92     ssize_t index = map.indexOfKey(pid);
93     if (index < 0) {
94         // new pid
95         ResourceInfos infosForPid;
96         map.add(pid, infosForPid);
97     }
98 
99     return map.editValueFor(pid);
100 }
101 
getResourceInfoForEdit(int64_t clientId,const sp<IResourceManagerClient> & client,ResourceInfos & infos)102 static ResourceInfo& getResourceInfoForEdit(
103         int64_t clientId,
104         const sp<IResourceManagerClient>& client,
105         ResourceInfos& infos) {
106     for (size_t i = 0; i < infos.size(); ++i) {
107         if (infos[i].clientId == clientId) {
108             return infos.editItemAt(i);
109         }
110     }
111     ResourceInfo info;
112     info.clientId = clientId;
113     info.client = client;
114     infos.push_back(info);
115     return infos.editItemAt(infos.size() - 1);
116 }
117 
notifyResourceGranted(int pid,const Vector<MediaResource> & resources)118 static void notifyResourceGranted(int pid, const Vector<MediaResource> &resources) {
119     static const char* const kServiceName = "media_resource_monitor";
120     sp<IBinder> binder = defaultServiceManager()->checkService(String16(kServiceName));
121     if (binder != NULL) {
122         sp<IMediaResourceMonitor> service = interface_cast<IMediaResourceMonitor>(binder);
123         for (size_t i = 0; i < resources.size(); ++i) {
124             if (resources[i].mSubType == MediaResource::kAudioCodec) {
125                 service->notifyResourceGranted(pid, IMediaResourceMonitor::TYPE_AUDIO_CODEC);
126             } else if (resources[i].mSubType == MediaResource::kVideoCodec) {
127                 service->notifyResourceGranted(pid, IMediaResourceMonitor::TYPE_VIDEO_CODEC);
128             }
129         }
130     }
131 }
132 
dump(int fd,const Vector<String16> &)133 status_t ResourceManagerService::dump(int fd, const Vector<String16>& /* args */) {
134     String8 result;
135 
136     if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
137         result.format("Permission Denial: "
138                 "can't dump ResourceManagerService from pid=%d, uid=%d\n",
139                 IPCThreadState::self()->getCallingPid(),
140                 IPCThreadState::self()->getCallingUid());
141         write(fd, result.string(), result.size());
142         return PERMISSION_DENIED;
143     }
144 
145     PidResourceInfosMap mapCopy;
146     bool supportsMultipleSecureCodecs;
147     bool supportsSecureWithNonSecureCodec;
148     String8 serviceLog;
149     {
150         Mutex::Autolock lock(mLock);
151         mapCopy = mMap;  // Shadow copy, real copy will happen on write.
152         supportsMultipleSecureCodecs = mSupportsMultipleSecureCodecs;
153         supportsSecureWithNonSecureCodec = mSupportsSecureWithNonSecureCodec;
154         serviceLog = mServiceLog->toString("    " /* linePrefix */);
155     }
156 
157     const size_t SIZE = 256;
158     char buffer[SIZE];
159     snprintf(buffer, SIZE, "ResourceManagerService: %p\n", this);
160     result.append(buffer);
161     result.append("  Policies:\n");
162     snprintf(buffer, SIZE, "    SupportsMultipleSecureCodecs: %d\n", supportsMultipleSecureCodecs);
163     result.append(buffer);
164     snprintf(buffer, SIZE, "    SupportsSecureWithNonSecureCodec: %d\n",
165             supportsSecureWithNonSecureCodec);
166     result.append(buffer);
167 
168     result.append("  Processes:\n");
169     for (size_t i = 0; i < mapCopy.size(); ++i) {
170         snprintf(buffer, SIZE, "    Pid: %d\n", mapCopy.keyAt(i));
171         result.append(buffer);
172 
173         const ResourceInfos &infos = mapCopy.valueAt(i);
174         for (size_t j = 0; j < infos.size(); ++j) {
175             result.append("      Client:\n");
176             snprintf(buffer, SIZE, "        Id: %lld\n", (long long)infos[j].clientId);
177             result.append(buffer);
178 
179             snprintf(buffer, SIZE, "        Name: %s\n", infos[j].client->getName().string());
180             result.append(buffer);
181 
182             Vector<MediaResource> resources = infos[j].resources;
183             result.append("        Resources:\n");
184             for (size_t k = 0; k < resources.size(); ++k) {
185                 snprintf(buffer, SIZE, "          %s\n", resources[k].toString().string());
186                 result.append(buffer);
187             }
188         }
189     }
190     result.append("  Events logs (most recent at top):\n");
191     result.append(serviceLog);
192 
193     write(fd, result.string(), result.size());
194     return OK;
195 }
196 
ResourceManagerService()197 ResourceManagerService::ResourceManagerService()
198     : ResourceManagerService(new ProcessInfo()) {}
199 
ResourceManagerService(sp<ProcessInfoInterface> processInfo)200 ResourceManagerService::ResourceManagerService(sp<ProcessInfoInterface> processInfo)
201     : mProcessInfo(processInfo),
202       mServiceLog(new ServiceLog()),
203       mSupportsMultipleSecureCodecs(true),
204       mSupportsSecureWithNonSecureCodec(true) {}
205 
~ResourceManagerService()206 ResourceManagerService::~ResourceManagerService() {}
207 
config(const Vector<MediaResourcePolicy> & policies)208 void ResourceManagerService::config(const Vector<MediaResourcePolicy> &policies) {
209     String8 log = String8::format("config(%s)", getString(policies).string());
210     mServiceLog->add(log);
211 
212     Mutex::Autolock lock(mLock);
213     for (size_t i = 0; i < policies.size(); ++i) {
214         String8 type = policies[i].mType;
215         String8 value = policies[i].mValue;
216         if (type == kPolicySupportsMultipleSecureCodecs) {
217             mSupportsMultipleSecureCodecs = (value == "true");
218         } else if (type == kPolicySupportsSecureWithNonSecureCodec) {
219             mSupportsSecureWithNonSecureCodec = (value == "true");
220         }
221     }
222 }
223 
addResource(int pid,int64_t clientId,const sp<IResourceManagerClient> client,const Vector<MediaResource> & resources)224 void ResourceManagerService::addResource(
225         int pid,
226         int64_t clientId,
227         const sp<IResourceManagerClient> client,
228         const Vector<MediaResource> &resources) {
229     String8 log = String8::format("addResource(pid %d, clientId %lld, resources %s)",
230             pid, (long long) clientId, getString(resources).string());
231     mServiceLog->add(log);
232 
233     Mutex::Autolock lock(mLock);
234     if (!mProcessInfo->isValidPid(pid)) {
235         ALOGE("Rejected addResource call with invalid pid.");
236         return;
237     }
238     ResourceInfos& infos = getResourceInfosForEdit(pid, mMap);
239     ResourceInfo& info = getResourceInfoForEdit(clientId, client, infos);
240     // TODO: do the merge instead of append.
241     info.resources.appendVector(resources);
242     if (info.deathNotifier == nullptr) {
243         info.deathNotifier = new DeathNotifier(this, pid, clientId);
244         IInterface::asBinder(client)->linkToDeath(info.deathNotifier);
245     }
246     notifyResourceGranted(pid, resources);
247 }
248 
removeResource(int pid,int64_t clientId)249 void ResourceManagerService::removeResource(int pid, int64_t clientId) {
250     removeResource(pid, clientId, true);
251 }
252 
removeResource(int pid,int64_t clientId,bool checkValid)253 void ResourceManagerService::removeResource(int pid, int64_t clientId, bool checkValid) {
254     String8 log = String8::format(
255             "removeResource(pid %d, clientId %lld)",
256             pid, (long long) clientId);
257     mServiceLog->add(log);
258 
259     Mutex::Autolock lock(mLock);
260     if (checkValid && !mProcessInfo->isValidPid(pid)) {
261         ALOGE("Rejected removeResource call with invalid pid.");
262         return;
263     }
264     ssize_t index = mMap.indexOfKey(pid);
265     if (index < 0) {
266         ALOGV("removeResource: didn't find pid %d for clientId %lld", pid, (long long) clientId);
267         return;
268     }
269     bool found = false;
270     ResourceInfos &infos = mMap.editValueAt(index);
271     for (size_t j = 0; j < infos.size(); ++j) {
272         if (infos[j].clientId == clientId) {
273             IInterface::asBinder(infos[j].client)->unlinkToDeath(infos[j].deathNotifier);
274             j = infos.removeAt(j);
275             found = true;
276             break;
277         }
278     }
279     if (!found) {
280         ALOGV("didn't find client");
281     }
282 }
283 
getClientForResource_l(int callingPid,const MediaResource * res,Vector<sp<IResourceManagerClient>> * clients)284 void ResourceManagerService::getClientForResource_l(
285         int callingPid, const MediaResource *res, Vector<sp<IResourceManagerClient>> *clients) {
286     if (res == NULL) {
287         return;
288     }
289     sp<IResourceManagerClient> client;
290     if (getLowestPriorityBiggestClient_l(callingPid, res->mType, &client)) {
291         clients->push_back(client);
292     }
293 }
294 
reclaimResource(int callingPid,const Vector<MediaResource> & resources)295 bool ResourceManagerService::reclaimResource(
296         int callingPid, const Vector<MediaResource> &resources) {
297     String8 log = String8::format("reclaimResource(callingPid %d, resources %s)",
298             callingPid, getString(resources).string());
299     mServiceLog->add(log);
300 
301     Vector<sp<IResourceManagerClient>> clients;
302     {
303         Mutex::Autolock lock(mLock);
304         if (!mProcessInfo->isValidPid(callingPid)) {
305             ALOGE("Rejected reclaimResource call with invalid callingPid.");
306             return false;
307         }
308         const MediaResource *secureCodec = NULL;
309         const MediaResource *nonSecureCodec = NULL;
310         const MediaResource *graphicMemory = NULL;
311         for (size_t i = 0; i < resources.size(); ++i) {
312             MediaResource::Type type = resources[i].mType;
313             if (resources[i].mType == MediaResource::kSecureCodec) {
314                 secureCodec = &resources[i];
315             } else if (type == MediaResource::kNonSecureCodec) {
316                 nonSecureCodec = &resources[i];
317             } else if (type == MediaResource::kGraphicMemory) {
318                 graphicMemory = &resources[i];
319             }
320         }
321 
322         // first pass to handle secure/non-secure codec conflict
323         if (secureCodec != NULL) {
324             if (!mSupportsMultipleSecureCodecs) {
325                 if (!getAllClients_l(callingPid, MediaResource::kSecureCodec, &clients)) {
326                     return false;
327                 }
328             }
329             if (!mSupportsSecureWithNonSecureCodec) {
330                 if (!getAllClients_l(callingPid, MediaResource::kNonSecureCodec, &clients)) {
331                     return false;
332                 }
333             }
334         }
335         if (nonSecureCodec != NULL) {
336             if (!mSupportsSecureWithNonSecureCodec) {
337                 if (!getAllClients_l(callingPid, MediaResource::kSecureCodec, &clients)) {
338                     return false;
339                 }
340             }
341         }
342 
343         if (clients.size() == 0) {
344             // if no secure/non-secure codec conflict, run second pass to handle other resources.
345             getClientForResource_l(callingPid, graphicMemory, &clients);
346         }
347 
348         if (clients.size() == 0) {
349             // if we are here, run the third pass to free one codec with the same type.
350             getClientForResource_l(callingPid, secureCodec, &clients);
351             getClientForResource_l(callingPid, nonSecureCodec, &clients);
352         }
353 
354         if (clients.size() == 0) {
355             // if we are here, run the fourth pass to free one codec with the different type.
356             if (secureCodec != NULL) {
357                 MediaResource temp(MediaResource::kNonSecureCodec, 1);
358                 getClientForResource_l(callingPid, &temp, &clients);
359             }
360             if (nonSecureCodec != NULL) {
361                 MediaResource temp(MediaResource::kSecureCodec, 1);
362                 getClientForResource_l(callingPid, &temp, &clients);
363             }
364         }
365     }
366 
367     if (clients.size() == 0) {
368         return false;
369     }
370 
371     sp<IResourceManagerClient> failedClient;
372     for (size_t i = 0; i < clients.size(); ++i) {
373         log = String8::format("reclaimResource from client %p", clients[i].get());
374         mServiceLog->add(log);
375         if (!clients[i]->reclaimResource()) {
376             failedClient = clients[i];
377             break;
378         }
379     }
380 
381     if (failedClient == NULL) {
382         return true;
383     }
384 
385     {
386         Mutex::Autolock lock(mLock);
387         bool found = false;
388         for (size_t i = 0; i < mMap.size(); ++i) {
389             ResourceInfos &infos = mMap.editValueAt(i);
390             for (size_t j = 0; j < infos.size();) {
391                 if (infos[j].client == failedClient) {
392                     j = infos.removeAt(j);
393                     found = true;
394                 } else {
395                     ++j;
396                 }
397             }
398             if (found) {
399                 break;
400             }
401         }
402         if (!found) {
403             ALOGV("didn't find failed client");
404         }
405     }
406 
407     return false;
408 }
409 
getAllClients_l(int callingPid,MediaResource::Type type,Vector<sp<IResourceManagerClient>> * clients)410 bool ResourceManagerService::getAllClients_l(
411         int callingPid, MediaResource::Type type, Vector<sp<IResourceManagerClient>> *clients) {
412     Vector<sp<IResourceManagerClient>> temp;
413     for (size_t i = 0; i < mMap.size(); ++i) {
414         ResourceInfos &infos = mMap.editValueAt(i);
415         for (size_t j = 0; j < infos.size(); ++j) {
416             if (hasResourceType(type, infos[j].resources)) {
417                 if (!isCallingPriorityHigher_l(callingPid, mMap.keyAt(i))) {
418                     // some higher/equal priority process owns the resource,
419                     // this request can't be fulfilled.
420                     ALOGE("getAllClients_l: can't reclaim resource %s from pid %d",
421                             asString(type), mMap.keyAt(i));
422                     return false;
423                 }
424                 temp.push_back(infos[j].client);
425             }
426         }
427     }
428     if (temp.size() == 0) {
429         ALOGV("getAllClients_l: didn't find any resource %s", asString(type));
430         return true;
431     }
432     clients->appendVector(temp);
433     return true;
434 }
435 
getLowestPriorityBiggestClient_l(int callingPid,MediaResource::Type type,sp<IResourceManagerClient> * client)436 bool ResourceManagerService::getLowestPriorityBiggestClient_l(
437         int callingPid, MediaResource::Type type, sp<IResourceManagerClient> *client) {
438     int lowestPriorityPid;
439     int lowestPriority;
440     int callingPriority;
441     if (!mProcessInfo->getPriority(callingPid, &callingPriority)) {
442         ALOGE("getLowestPriorityBiggestClient_l: can't get process priority for pid %d",
443                 callingPid);
444         return false;
445     }
446     if (!getLowestPriorityPid_l(type, &lowestPriorityPid, &lowestPriority)) {
447         return false;
448     }
449     if (lowestPriority <= callingPriority) {
450         ALOGE("getLowestPriorityBiggestClient_l: lowest priority %d vs caller priority %d",
451                 lowestPriority, callingPriority);
452         return false;
453     }
454 
455     if (!getBiggestClient_l(lowestPriorityPid, type, client)) {
456         return false;
457     }
458     return true;
459 }
460 
getLowestPriorityPid_l(MediaResource::Type type,int * lowestPriorityPid,int * lowestPriority)461 bool ResourceManagerService::getLowestPriorityPid_l(
462         MediaResource::Type type, int *lowestPriorityPid, int *lowestPriority) {
463     int pid = -1;
464     int priority = -1;
465     for (size_t i = 0; i < mMap.size(); ++i) {
466         if (mMap.valueAt(i).size() == 0) {
467             // no client on this process.
468             continue;
469         }
470         if (!hasResourceType(type, mMap.valueAt(i))) {
471             // doesn't have the requested resource type
472             continue;
473         }
474         int tempPid = mMap.keyAt(i);
475         int tempPriority;
476         if (!mProcessInfo->getPriority(tempPid, &tempPriority)) {
477             ALOGV("getLowestPriorityPid_l: can't get priority of pid %d, skipped", tempPid);
478             // TODO: remove this pid from mMap?
479             continue;
480         }
481         if (pid == -1 || tempPriority > priority) {
482             // initial the value
483             pid = tempPid;
484             priority = tempPriority;
485         }
486     }
487     if (pid != -1) {
488         *lowestPriorityPid = pid;
489         *lowestPriority = priority;
490     }
491     return (pid != -1);
492 }
493 
isCallingPriorityHigher_l(int callingPid,int pid)494 bool ResourceManagerService::isCallingPriorityHigher_l(int callingPid, int pid) {
495     int callingPidPriority;
496     if (!mProcessInfo->getPriority(callingPid, &callingPidPriority)) {
497         return false;
498     }
499 
500     int priority;
501     if (!mProcessInfo->getPriority(pid, &priority)) {
502         return false;
503     }
504 
505     return (callingPidPriority < priority);
506 }
507 
getBiggestClient_l(int pid,MediaResource::Type type,sp<IResourceManagerClient> * client)508 bool ResourceManagerService::getBiggestClient_l(
509         int pid, MediaResource::Type type, sp<IResourceManagerClient> *client) {
510     ssize_t index = mMap.indexOfKey(pid);
511     if (index < 0) {
512         ALOGE("getBiggestClient_l: can't find resource info for pid %d", pid);
513         return false;
514     }
515 
516     sp<IResourceManagerClient> clientTemp;
517     uint64_t largestValue = 0;
518     const ResourceInfos &infos = mMap.valueAt(index);
519     for (size_t i = 0; i < infos.size(); ++i) {
520         Vector<MediaResource> resources = infos[i].resources;
521         for (size_t j = 0; j < resources.size(); ++j) {
522             if (resources[j].mType == type) {
523                 if (resources[j].mValue > largestValue) {
524                     largestValue = resources[j].mValue;
525                     clientTemp = infos[i].client;
526                 }
527             }
528         }
529     }
530 
531     if (clientTemp == NULL) {
532         ALOGE("getBiggestClient_l: can't find resource type %s for pid %d", asString(type), pid);
533         return false;
534     }
535 
536     *client = clientTemp;
537     return true;
538 }
539 
540 } // namespace android
541