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