Home | History | Annotate | Download | only in mediaresourcemanager
      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:
     41     DeathNotifier(const wp<ResourceManagerService> &service, int pid, int64_t clientId)
     42         : mService(service), mPid(pid), mClientId(clientId) {}
     43 
     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>
     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 
     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 
     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 
     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 
    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 
    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 
    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 
    197 ResourceManagerService::ResourceManagerService()
    198     : ResourceManagerService(new ProcessInfo()) {}
    199 
    200 ResourceManagerService::ResourceManagerService(sp<ProcessInfoInterface> processInfo)
    201     : mProcessInfo(processInfo),
    202       mServiceLog(new ServiceLog()),
    203       mSupportsMultipleSecureCodecs(true),
    204       mSupportsSecureWithNonSecureCodec(true) {}
    205 
    206 ResourceManagerService::~ResourceManagerService() {}
    207 
    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 
    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 
    249 void ResourceManagerService::removeResource(int pid, int64_t clientId) {
    250     removeResource(pid, clientId, true);
    251 }
    252 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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