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