/* ** ** Copyright 2015, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. ** You may obtain a copy of the License at ** ** http://www.apache.org/licenses/LICENSE-2.0 ** ** Unless required by applicable law or agreed to in writing, software ** distributed under the License is distributed on an "AS IS" BASIS, ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** See the License for the specific language governing permissions and ** limitations under the License. */ //#define LOG_NDEBUG 0 #define LOG_TAG "ResourceManagerService" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "IMediaResourceMonitor.h" #include "ResourceManagerService.h" #include "ResourceObserverService.h" #include "ServiceLog.h" namespace android { //static std::mutex ResourceManagerService::sCookieLock; //static uintptr_t ResourceManagerService::sCookieCounter = 0; //static std::map > ResourceManagerService::sCookieToDeathNotifierMap; class DeathNotifier : public RefBase { public: DeathNotifier(const std::shared_ptr &service, int pid, int64_t clientId); virtual ~DeathNotifier() {} // Implement death recipient static void BinderDiedCallback(void* cookie); virtual void binderDied(); protected: std::weak_ptr mService; int mPid; int64_t mClientId; }; DeathNotifier::DeathNotifier(const std::shared_ptr &service, int pid, int64_t clientId) : mService(service), mPid(pid), mClientId(clientId) {} //static void DeathNotifier::BinderDiedCallback(void* cookie) { sp notifier; { std::scoped_lock lock{ResourceManagerService::sCookieLock}; auto it = ResourceManagerService::sCookieToDeathNotifierMap.find( reinterpret_cast(cookie)); if (it == ResourceManagerService::sCookieToDeathNotifierMap.end()) { return; } notifier = it->second; } if (notifier.get() != nullptr) { notifier->binderDied(); } } void DeathNotifier::binderDied() { // Don't check for pid validity since we know it's already dead. std::shared_ptr service = mService.lock(); if (service == nullptr) { ALOGW("ResourceManagerService is dead as well."); return; } service->overridePid(mPid, -1); // thiz is freed in the call below, so it must be last call referring thiz service->removeResource(mPid, mClientId, false /*checkValid*/); } class OverrideProcessInfoDeathNotifier : public DeathNotifier { public: OverrideProcessInfoDeathNotifier(const std::shared_ptr &service, int pid) : DeathNotifier(service, pid, 0) {} virtual ~OverrideProcessInfoDeathNotifier() {} virtual void binderDied(); }; void OverrideProcessInfoDeathNotifier::binderDied() { // Don't check for pid validity since we know it's already dead. std::shared_ptr service = mService.lock(); if (service == nullptr) { ALOGW("ResourceManagerService is dead as well."); return; } service->removeProcessInfoOverride(mPid); } template static String8 getString(const std::vector &items) { String8 itemsStr; for (size_t i = 0; i < items.size(); ++i) { itemsStr.appendFormat("%s ", toString(items[i]).string()); } return itemsStr; } static bool hasResourceType(MediaResource::Type type, const ResourceList& resources) { for (auto it = resources.begin(); it != resources.end(); it++) { if (it->second.type == type) { return true; } } return false; } static bool hasResourceType(MediaResource::Type type, const ResourceInfos& infos) { for (size_t i = 0; i < infos.size(); ++i) { if (hasResourceType(type, infos[i].resources)) { return true; } } return false; } static ResourceInfos& getResourceInfosForEdit( int pid, PidResourceInfosMap& map) { ssize_t index = map.indexOfKey(pid); if (index < 0) { // new pid ResourceInfos infosForPid; map.add(pid, infosForPid); } return map.editValueFor(pid); } static ResourceInfo& getResourceInfoForEdit( uid_t uid, int64_t clientId, const std::shared_ptr& client, ResourceInfos& infos) { ssize_t index = infos.indexOfKey(clientId); if (index < 0) { ResourceInfo info; info.uid = uid; info.clientId = clientId; info.client = client; info.cookie = 0; info.pendingRemoval = false; index = infos.add(clientId, info); } return infos.editValueAt(index); } static void notifyResourceGranted(int pid, const std::vector &resources) { static const char* const kServiceName = "media_resource_monitor"; sp binder = defaultServiceManager()->checkService(String16(kServiceName)); if (binder != NULL) { sp service = interface_cast(binder); for (size_t i = 0; i < resources.size(); ++i) { if (resources[i].subType == MediaResource::SubType::kAudioCodec) { service->notifyResourceGranted(pid, IMediaResourceMonitor::TYPE_AUDIO_CODEC); } else if (resources[i].subType == MediaResource::SubType::kVideoCodec) { service->notifyResourceGranted(pid, IMediaResourceMonitor::TYPE_VIDEO_CODEC); } } } } binder_status_t ResourceManagerService::dump( int fd, const char** /*args*/, uint32_t /*numArgs*/) { String8 result; if (checkCallingPermission(String16("android.permission.DUMP")) == false) { result.format("Permission Denial: " "can't dump ResourceManagerService from pid=%d, uid=%d\n", AIBinder_getCallingPid(), AIBinder_getCallingUid()); write(fd, result.string(), result.size()); return PERMISSION_DENIED; } PidResourceInfosMap mapCopy; bool supportsMultipleSecureCodecs; bool supportsSecureWithNonSecureCodec; std::map overridePidMapCopy; String8 serviceLog; { Mutex::Autolock lock(mLock); mapCopy = mMap; // Shadow copy, real copy will happen on write. supportsMultipleSecureCodecs = mSupportsMultipleSecureCodecs; supportsSecureWithNonSecureCodec = mSupportsSecureWithNonSecureCodec; serviceLog = mServiceLog->toString(" " /* linePrefix */); overridePidMapCopy = mOverridePidMap; } const size_t SIZE = 256; char buffer[SIZE]; snprintf(buffer, SIZE, "ResourceManagerService: %p\n", this); result.append(buffer); result.append(" Policies:\n"); snprintf(buffer, SIZE, " SupportsMultipleSecureCodecs: %d\n", supportsMultipleSecureCodecs); result.append(buffer); snprintf(buffer, SIZE, " SupportsSecureWithNonSecureCodec: %d\n", supportsSecureWithNonSecureCodec); result.append(buffer); result.append(" Processes:\n"); for (size_t i = 0; i < mapCopy.size(); ++i) { snprintf(buffer, SIZE, " Pid: %d\n", mapCopy.keyAt(i)); result.append(buffer); const ResourceInfos &infos = mapCopy.valueAt(i); for (size_t j = 0; j < infos.size(); ++j) { result.append(" Client:\n"); snprintf(buffer, SIZE, " Id: %lld\n", (long long)infos[j].clientId); result.append(buffer); std::string clientName; Status status = infos[j].client->getName(&clientName); if (!status.isOk()) { clientName = ""; } snprintf(buffer, SIZE, " Name: %s\n", clientName.c_str()); result.append(buffer); const ResourceList &resources = infos[j].resources; result.append(" Resources:\n"); for (auto it = resources.begin(); it != resources.end(); it++) { snprintf(buffer, SIZE, " %s\n", toString(it->second).string()); result.append(buffer); } } } result.append(" Process Pid override:\n"); for (auto it = overridePidMapCopy.begin(); it != overridePidMapCopy.end(); ++it) { snprintf(buffer, SIZE, " Original Pid: %d, Override Pid: %d\n", it->first, it->second); result.append(buffer); } result.append(" Events logs (most recent at top):\n"); result.append(serviceLog); write(fd, result.string(), result.size()); return OK; } struct SystemCallbackImpl : public ResourceManagerService::SystemCallbackInterface { SystemCallbackImpl() : mClientToken(new BBinder()) {} virtual void noteStartVideo(int uid) override { BatteryNotifier::getInstance().noteStartVideo(uid); } virtual void noteStopVideo(int uid) override { BatteryNotifier::getInstance().noteStopVideo(uid); } virtual void noteResetVideo() override { BatteryNotifier::getInstance().noteResetVideo(); } virtual bool requestCpusetBoost(bool enable) override { return android::requestCpusetBoost(enable, mClientToken); } protected: virtual ~SystemCallbackImpl() {} private: DISALLOW_EVIL_CONSTRUCTORS(SystemCallbackImpl); sp mClientToken; }; ResourceManagerService::ResourceManagerService() : ResourceManagerService(new ProcessInfo(), new SystemCallbackImpl()) {} ResourceManagerService::ResourceManagerService( const sp &processInfo, const sp &systemResource) : mProcessInfo(processInfo), mSystemCB(systemResource), mServiceLog(new ServiceLog()), mSupportsMultipleSecureCodecs(true), mSupportsSecureWithNonSecureCodec(true), mCpuBoostCount(0), mDeathRecipient(AIBinder_DeathRecipient_new(DeathNotifier::BinderDiedCallback)) { mSystemCB->noteResetVideo(); } //static void ResourceManagerService::instantiate() { std::shared_ptr service = ::ndk::SharedRefBase::make(); binder_status_t status = AServiceManager_addService(service->asBinder().get(), getServiceName()); if (status != STATUS_OK) { return; } std::shared_ptr observerService = ResourceObserverService::instantiate(); if (observerService != nullptr) { service->setObserverService(observerService); } // TODO: mediaserver main() is already starting the thread pool, // move this to mediaserver main() when other services in mediaserver // are converted to ndk-platform aidl. //ABinderProcess_startThreadPool(); } ResourceManagerService::~ResourceManagerService() {} void ResourceManagerService::setObserverService( const std::shared_ptr& observerService) { mObserverService = observerService; } Status ResourceManagerService::config(const std::vector& policies) { String8 log = String8::format("config(%s)", getString(policies).string()); mServiceLog->add(log); Mutex::Autolock lock(mLock); for (size_t i = 0; i < policies.size(); ++i) { const std::string &type = policies[i].type; const std::string &value = policies[i].value; if (type == MediaResourcePolicy::kPolicySupportsMultipleSecureCodecs()) { mSupportsMultipleSecureCodecs = (value == "true"); } else if (type == MediaResourcePolicy::kPolicySupportsSecureWithNonSecureCodec()) { mSupportsSecureWithNonSecureCodec = (value == "true"); } } return Status::ok(); } void ResourceManagerService::onFirstAdded( const MediaResourceParcel& resource, const ResourceInfo& clientInfo) { // first time added if (resource.type == MediaResource::Type::kCpuBoost && resource.subType == MediaResource::SubType::kUnspecifiedSubType) { // Request it on every new instance of kCpuBoost, as the media.codec // could have died, if we only do it the first time subsequent instances // never gets the boost. if (mSystemCB->requestCpusetBoost(true) != OK) { ALOGW("couldn't request cpuset boost"); } mCpuBoostCount++; } else if (resource.type == MediaResource::Type::kBattery && resource.subType == MediaResource::SubType::kVideoCodec) { mSystemCB->noteStartVideo(clientInfo.uid); } } void ResourceManagerService::onLastRemoved( const MediaResourceParcel& resource, const ResourceInfo& clientInfo) { if (resource.type == MediaResource::Type::kCpuBoost && resource.subType == MediaResource::SubType::kUnspecifiedSubType && mCpuBoostCount > 0) { if (--mCpuBoostCount == 0) { mSystemCB->requestCpusetBoost(false); } } else if (resource.type == MediaResource::Type::kBattery && resource.subType == MediaResource::SubType::kVideoCodec) { mSystemCB->noteStopVideo(clientInfo.uid); } } void ResourceManagerService::mergeResources( MediaResourceParcel& r1, const MediaResourceParcel& r2) { // The resource entry on record is maintained to be in [0,INT64_MAX]. // Clamp if merging in the new resource value causes it to go out of bound. // Note that the new resource value could be negative, eg.DrmSession, the // value goes lower when the session is used more often. During reclaim // the session with the highest value (lowest usage) would be closed. if (r2.value < INT64_MAX - r1.value) { r1.value += r2.value; if (r1.value < 0) { r1.value = 0; } } else { r1.value = INT64_MAX; } } Status ResourceManagerService::addResource( int32_t pid, int32_t uid, int64_t clientId, const std::shared_ptr& client, const std::vector& resources) { String8 log = String8::format("addResource(pid %d, clientId %lld, resources %s)", pid, (long long) clientId, getString(resources).string()); mServiceLog->add(log); Mutex::Autolock lock(mLock); if (!mProcessInfo->isValidPid(pid)) { pid_t callingPid = IPCThreadState::self()->getCallingPid(); uid_t callingUid = IPCThreadState::self()->getCallingUid(); ALOGW("%s called with untrusted pid %d, using calling pid %d, uid %d", __FUNCTION__, pid, callingPid, callingUid); pid = callingPid; uid = callingUid; } ResourceInfos& infos = getResourceInfosForEdit(pid, mMap); ResourceInfo& info = getResourceInfoForEdit(uid, clientId, client, infos); ResourceList resourceAdded; for (size_t i = 0; i < resources.size(); ++i) { const auto &res = resources[i]; const auto resType = std::tuple(res.type, res.subType, res.id); if (res.value < 0 && res.type != MediaResource::Type::kDrmSession) { ALOGW("Ignoring request to remove negative value of non-drm resource"); continue; } if (info.resources.find(resType) == info.resources.end()) { if (res.value <= 0) { // We can't init a new entry with negative value, although it's allowed // to merge in negative values after the initial add. ALOGW("Ignoring request to add new resource entry with value <= 0"); continue; } onFirstAdded(res, info); info.resources[resType] = res; } else { mergeResources(info.resources[resType], res); } // Add it to the list of added resources for observers. auto it = resourceAdded.find(resType); if (it == resourceAdded.end()) { resourceAdded[resType] = res; } else { mergeResources(it->second, res); } } if (info.cookie == 0 && client != nullptr) { info.cookie = addCookieAndLink_l(client->asBinder(), new DeathNotifier(ref(), pid, clientId)); } if (mObserverService != nullptr && !resourceAdded.empty()) { mObserverService->onResourceAdded(uid, pid, resourceAdded); } notifyResourceGranted(pid, resources); return Status::ok(); } Status ResourceManagerService::removeResource( int32_t pid, int64_t clientId, const std::vector& resources) { String8 log = String8::format("removeResource(pid %d, clientId %lld, resources %s)", pid, (long long) clientId, getString(resources).string()); mServiceLog->add(log); Mutex::Autolock lock(mLock); if (!mProcessInfo->isValidPid(pid)) { pid_t callingPid = IPCThreadState::self()->getCallingPid(); ALOGW("%s called with untrusted pid %d, using calling pid %d", __FUNCTION__, pid, callingPid); pid = callingPid; } ssize_t index = mMap.indexOfKey(pid); if (index < 0) { ALOGV("removeResource: didn't find pid %d for clientId %lld", pid, (long long) clientId); return Status::ok(); } ResourceInfos &infos = mMap.editValueAt(index); index = infos.indexOfKey(clientId); if (index < 0) { ALOGV("removeResource: didn't find clientId %lld", (long long) clientId); return Status::ok(); } ResourceInfo &info = infos.editValueAt(index); ResourceList resourceRemoved; for (size_t i = 0; i < resources.size(); ++i) { const auto &res = resources[i]; const auto resType = std::tuple(res.type, res.subType, res.id); if (res.value < 0) { ALOGW("Ignoring request to remove negative value of resource"); continue; } // ignore if we don't have it if (info.resources.find(resType) != info.resources.end()) { MediaResourceParcel &resource = info.resources[resType]; MediaResourceParcel actualRemoved = res; if (resource.value > res.value) { resource.value -= res.value; } else { onLastRemoved(res, info); actualRemoved.value = resource.value; info.resources.erase(resType); } // Add it to the list of removed resources for observers. auto it = resourceRemoved.find(resType); if (it == resourceRemoved.end()) { resourceRemoved[resType] = actualRemoved; } else { mergeResources(it->second, actualRemoved); } } } if (mObserverService != nullptr && !resourceRemoved.empty()) { mObserverService->onResourceRemoved(info.uid, pid, resourceRemoved); } return Status::ok(); } Status ResourceManagerService::removeClient(int32_t pid, int64_t clientId) { removeResource(pid, clientId, true /*checkValid*/); return Status::ok(); } Status ResourceManagerService::removeResource(int pid, int64_t clientId, bool checkValid) { String8 log = String8::format( "removeResource(pid %d, clientId %lld)", pid, (long long) clientId); mServiceLog->add(log); Mutex::Autolock lock(mLock); if (checkValid && !mProcessInfo->isValidPid(pid)) { pid_t callingPid = IPCThreadState::self()->getCallingPid(); ALOGW("%s called with untrusted pid %d, using calling pid %d", __FUNCTION__, pid, callingPid); pid = callingPid; } ssize_t index = mMap.indexOfKey(pid); if (index < 0) { ALOGV("removeResource: didn't find pid %d for clientId %lld", pid, (long long) clientId); return Status::ok(); } ResourceInfos &infos = mMap.editValueAt(index); index = infos.indexOfKey(clientId); if (index < 0) { ALOGV("removeResource: didn't find clientId %lld", (long long) clientId); return Status::ok(); } const ResourceInfo &info = infos[index]; for (auto it = info.resources.begin(); it != info.resources.end(); it++) { onLastRemoved(it->second, info); } removeCookieAndUnlink_l(info.client->asBinder(), info.cookie); if (mObserverService != nullptr && !info.resources.empty()) { mObserverService->onResourceRemoved(info.uid, pid, info.resources); } infos.removeItemsAt(index); return Status::ok(); } void ResourceManagerService::getClientForResource_l( int callingPid, const MediaResourceParcel *res, Vector> *clients) { if (res == NULL) { return; } std::shared_ptr client; if (getLowestPriorityBiggestClient_l(callingPid, res->type, &client)) { clients->push_back(client); } } Status ResourceManagerService::reclaimResource( int32_t callingPid, const std::vector& resources, bool* _aidl_return) { String8 log = String8::format("reclaimResource(callingPid %d, resources %s)", callingPid, getString(resources).string()); mServiceLog->add(log); *_aidl_return = false; Vector> clients; { Mutex::Autolock lock(mLock); if (!mProcessInfo->isValidPid(callingPid)) { pid_t actualCallingPid = IPCThreadState::self()->getCallingPid(); ALOGW("%s called with untrusted pid %d, using actual calling pid %d", __FUNCTION__, callingPid, actualCallingPid); callingPid = actualCallingPid; } const MediaResourceParcel *secureCodec = NULL; const MediaResourceParcel *nonSecureCodec = NULL; const MediaResourceParcel *graphicMemory = NULL; const MediaResourceParcel *drmSession = NULL; for (size_t i = 0; i < resources.size(); ++i) { MediaResource::Type type = resources[i].type; if (resources[i].type == MediaResource::Type::kSecureCodec) { secureCodec = &resources[i]; } else if (type == MediaResource::Type::kNonSecureCodec) { nonSecureCodec = &resources[i]; } else if (type == MediaResource::Type::kGraphicMemory) { graphicMemory = &resources[i]; } else if (type == MediaResource::Type::kDrmSession) { drmSession = &resources[i]; } } // first pass to handle secure/non-secure codec conflict if (secureCodec != NULL) { if (!mSupportsMultipleSecureCodecs) { if (!getAllClients_l(callingPid, MediaResource::Type::kSecureCodec, &clients)) { return Status::ok(); } } if (!mSupportsSecureWithNonSecureCodec) { if (!getAllClients_l(callingPid, MediaResource::Type::kNonSecureCodec, &clients)) { return Status::ok(); } } } if (nonSecureCodec != NULL) { if (!mSupportsSecureWithNonSecureCodec) { if (!getAllClients_l(callingPid, MediaResource::Type::kSecureCodec, &clients)) { return Status::ok(); } } } if (drmSession != NULL) { getClientForResource_l(callingPid, drmSession, &clients); if (clients.size() == 0) { return Status::ok(); } } if (clients.size() == 0) { // if no secure/non-secure codec conflict, run second pass to handle other resources. getClientForResource_l(callingPid, graphicMemory, &clients); } if (clients.size() == 0) { // if we are here, run the third pass to free one codec with the same type. getClientForResource_l(callingPid, secureCodec, &clients); getClientForResource_l(callingPid, nonSecureCodec, &clients); } if (clients.size() == 0) { // if we are here, run the fourth pass to free one codec with the different type. if (secureCodec != NULL) { MediaResource temp(MediaResource::Type::kNonSecureCodec, 1); getClientForResource_l(callingPid, &temp, &clients); } if (nonSecureCodec != NULL) { MediaResource temp(MediaResource::Type::kSecureCodec, 1); getClientForResource_l(callingPid, &temp, &clients); } } } *_aidl_return = reclaimInternal(clients); return Status::ok(); } bool ResourceManagerService::reclaimInternal( const Vector> &clients) { if (clients.size() == 0) { return false; } std::shared_ptr failedClient; for (size_t i = 0; i < clients.size(); ++i) { String8 log = String8::format("reclaimResource from client %p", clients[i].get()); mServiceLog->add(log); bool success; Status status = clients[i]->reclaimResource(&success); if (!status.isOk() || !success) { failedClient = clients[i]; break; } } if (failedClient == NULL) { return true; } { Mutex::Autolock lock(mLock); bool found = false; for (size_t i = 0; i < mMap.size(); ++i) { ResourceInfos &infos = mMap.editValueAt(i); for (size_t j = 0; j < infos.size();) { if (infos[j].client == failedClient) { j = infos.removeItemsAt(j); found = true; } else { ++j; } } if (found) { break; } } if (!found) { ALOGV("didn't find failed client"); } } return false; } Status ResourceManagerService::overridePid( int originalPid, int newPid) { String8 log = String8::format("overridePid(originalPid %d, newPid %d)", originalPid, newPid); mServiceLog->add(log); // allow if this is called from the same process or the process has // permission. if ((AIBinder_getCallingPid() != getpid()) && (checkCallingPermission(String16( "android.permission.MEDIA_RESOURCE_OVERRIDE_PID")) == false)) { ALOGE( "Permission Denial: can't access overridePid method from pid=%d, " "self pid=%d\n", AIBinder_getCallingPid(), getpid()); return Status::fromServiceSpecificError(PERMISSION_DENIED); } { Mutex::Autolock lock(mLock); mOverridePidMap.erase(originalPid); if (newPid != -1) { mOverridePidMap.emplace(originalPid, newPid); } } return Status::ok(); } Status ResourceManagerService::overrideProcessInfo( const std::shared_ptr& client, int pid, int procState, int oomScore) { String8 log = String8::format("overrideProcessInfo(pid %d, procState %d, oomScore %d)", pid, procState, oomScore); mServiceLog->add(log); // Only allow the override if the caller already can access process state and oom scores. int callingPid = AIBinder_getCallingPid(); if (callingPid != getpid() && (callingPid != pid || !checkCallingPermission(String16( "android.permission.GET_PROCESS_STATE_AND_OOM_SCORE")))) { ALOGE("Permission Denial: overrideProcessInfo method from pid=%d", callingPid); return Status::fromServiceSpecificError(PERMISSION_DENIED); } if (client == nullptr) { return Status::fromServiceSpecificError(BAD_VALUE); } Mutex::Autolock lock(mLock); removeProcessInfoOverride_l(pid); if (!mProcessInfo->overrideProcessInfo(pid, procState, oomScore)) { // Override value is rejected by ProcessInfo. return Status::fromServiceSpecificError(BAD_VALUE); } uintptr_t cookie = addCookieAndLink_l(client->asBinder(), new OverrideProcessInfoDeathNotifier(ref(), pid)); mProcessInfoOverrideMap.emplace(pid, ProcessInfoOverride{cookie, client}); return Status::ok(); } uintptr_t ResourceManagerService::addCookieAndLink_l( ::ndk::SpAIBinder binder, const sp& notifier) { std::scoped_lock lock{sCookieLock}; uintptr_t cookie; // Need to skip cookie 0 (if it wraps around). ResourceInfo has cookie initialized to 0 // indicating the death notifier is not created yet. while ((cookie = ++sCookieCounter) == 0); AIBinder_linkToDeath(binder.get(), mDeathRecipient.get(), (void*)cookie); sCookieToDeathNotifierMap.emplace(cookie, notifier); return cookie; } void ResourceManagerService::removeCookieAndUnlink_l( ::ndk::SpAIBinder binder, uintptr_t cookie) { std::scoped_lock lock{sCookieLock}; AIBinder_unlinkToDeath(binder.get(), mDeathRecipient.get(), (void*)cookie); sCookieToDeathNotifierMap.erase(cookie); } void ResourceManagerService::removeProcessInfoOverride(int pid) { Mutex::Autolock lock(mLock); removeProcessInfoOverride_l(pid); } void ResourceManagerService::removeProcessInfoOverride_l(int pid) { auto it = mProcessInfoOverrideMap.find(pid); if (it == mProcessInfoOverrideMap.end()) { return; } mProcessInfo->removeProcessInfoOverride(pid); removeCookieAndUnlink_l(it->second.client->asBinder(), it->second.cookie); mProcessInfoOverrideMap.erase(pid); } Status ResourceManagerService::markClientForPendingRemoval(int32_t pid, int64_t clientId) { String8 log = String8::format( "markClientForPendingRemoval(pid %d, clientId %lld)", pid, (long long) clientId); mServiceLog->add(log); Mutex::Autolock lock(mLock); if (!mProcessInfo->isValidPid(pid)) { pid_t callingPid = IPCThreadState::self()->getCallingPid(); ALOGW("%s called with untrusted pid %d, using calling pid %d", __FUNCTION__, pid, callingPid); pid = callingPid; } ssize_t index = mMap.indexOfKey(pid); if (index < 0) { ALOGV("markClientForPendingRemoval: didn't find pid %d for clientId %lld", pid, (long long)clientId); return Status::ok(); } ResourceInfos &infos = mMap.editValueAt(index); index = infos.indexOfKey(clientId); if (index < 0) { ALOGV("markClientForPendingRemoval: didn't find clientId %lld", (long long) clientId); return Status::ok(); } ResourceInfo &info = infos.editValueAt(index); info.pendingRemoval = true; return Status::ok(); } Status ResourceManagerService::reclaimResourcesFromClientsPendingRemoval(int32_t pid) { String8 log = String8::format("reclaimResourcesFromClientsPendingRemoval(pid %d)", pid); mServiceLog->add(log); Vector> clients; { Mutex::Autolock lock(mLock); if (!mProcessInfo->isValidPid(pid)) { pid_t callingPid = IPCThreadState::self()->getCallingPid(); ALOGW("%s called with untrusted pid %d, using calling pid %d", __FUNCTION__, pid, callingPid); pid = callingPid; } for (MediaResource::Type type : {MediaResource::Type::kSecureCodec, MediaResource::Type::kNonSecureCodec, MediaResource::Type::kGraphicMemory, MediaResource::Type::kDrmSession}) { std::shared_ptr client; if (getBiggestClient_l(pid, type, &client, true /* pendingRemovalOnly */)) { clients.add(client); break; } } } if (!clients.empty()) { reclaimInternal(clients); } return Status::ok(); } bool ResourceManagerService::getPriority_l(int pid, int* priority) { int newPid = pid; if (mOverridePidMap.find(pid) != mOverridePidMap.end()) { newPid = mOverridePidMap[pid]; ALOGD("getPriority_l: use override pid %d instead original pid %d", newPid, pid); } return mProcessInfo->getPriority(newPid, priority); } bool ResourceManagerService::getAllClients_l( int callingPid, MediaResource::Type type, Vector> *clients) { Vector> temp; for (size_t i = 0; i < mMap.size(); ++i) { ResourceInfos &infos = mMap.editValueAt(i); for (size_t j = 0; j < infos.size(); ++j) { if (hasResourceType(type, infos[j].resources)) { if (!isCallingPriorityHigher_l(callingPid, mMap.keyAt(i))) { // some higher/equal priority process owns the resource, // this request can't be fulfilled. ALOGE("getAllClients_l: can't reclaim resource %s from pid %d", asString(type), mMap.keyAt(i)); return false; } temp.push_back(infos[j].client); } } } if (temp.size() == 0) { ALOGV("getAllClients_l: didn't find any resource %s", asString(type)); return true; } clients->appendVector(temp); return true; } bool ResourceManagerService::getLowestPriorityBiggestClient_l( int callingPid, MediaResource::Type type, std::shared_ptr *client) { int lowestPriorityPid; int lowestPriority; int callingPriority; // Before looking into other processes, check if we have clients marked for // pending removal in the same process. if (getBiggestClient_l(callingPid, type, client, true /* pendingRemovalOnly */)) { return true; } if (!getPriority_l(callingPid, &callingPriority)) { ALOGE("getLowestPriorityBiggestClient_l: can't get process priority for pid %d", callingPid); return false; } if (!getLowestPriorityPid_l(type, &lowestPriorityPid, &lowestPriority)) { return false; } if (lowestPriority <= callingPriority) { ALOGE("getLowestPriorityBiggestClient_l: lowest priority %d vs caller priority %d", lowestPriority, callingPriority); return false; } if (!getBiggestClient_l(lowestPriorityPid, type, client)) { return false; } return true; } bool ResourceManagerService::getLowestPriorityPid_l( MediaResource::Type type, int *lowestPriorityPid, int *lowestPriority) { int pid = -1; int priority = -1; for (size_t i = 0; i < mMap.size(); ++i) { if (mMap.valueAt(i).size() == 0) { // no client on this process. continue; } if (!hasResourceType(type, mMap.valueAt(i))) { // doesn't have the requested resource type continue; } int tempPid = mMap.keyAt(i); int tempPriority; if (!getPriority_l(tempPid, &tempPriority)) { ALOGV("getLowestPriorityPid_l: can't get priority of pid %d, skipped", tempPid); // TODO: remove this pid from mMap? continue; } if (pid == -1 || tempPriority > priority) { // initial the value pid = tempPid; priority = tempPriority; } } if (pid != -1) { *lowestPriorityPid = pid; *lowestPriority = priority; } return (pid != -1); } bool ResourceManagerService::isCallingPriorityHigher_l(int callingPid, int pid) { int callingPidPriority; if (!getPriority_l(callingPid, &callingPidPriority)) { return false; } int priority; if (!getPriority_l(pid, &priority)) { return false; } return (callingPidPriority < priority); } bool ResourceManagerService::getBiggestClient_l( int pid, MediaResource::Type type, std::shared_ptr *client, bool pendingRemovalOnly) { ssize_t index = mMap.indexOfKey(pid); if (index < 0) { ALOGE_IF(!pendingRemovalOnly, "getBiggestClient_l: can't find resource info for pid %d", pid); return false; } std::shared_ptr clientTemp; uint64_t largestValue = 0; const ResourceInfos &infos = mMap.valueAt(index); for (size_t i = 0; i < infos.size(); ++i) { const ResourceList &resources = infos[i].resources; if (pendingRemovalOnly && !infos[i].pendingRemoval) { continue; } for (auto it = resources.begin(); it != resources.end(); it++) { const MediaResourceParcel &resource = it->second; if (resource.type == type) { if (resource.value > largestValue) { largestValue = resource.value; clientTemp = infos[i].client; } } } } if (clientTemp == NULL) { ALOGE_IF(!pendingRemovalOnly, "getBiggestClient_l: can't find resource type %s for pid %d", asString(type), pid); return false; } *client = clientTemp; return true; } } // namespace android