/* ** ** 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 "ResourceManagerMetrics.h" #include "ResourceManagerServiceNew.h" #include "ResourceObserverService.h" #include "ServiceLog.h" namespace CodecFeatureFlags = com::android::media::codec::flags; namespace android { void ResourceManagerService::getResourceDump(std::string& resourceLog) const { PidResourceInfosMap mapCopy; std::map overridePidMapCopy; { std::scoped_lock lock{mLock}; mapCopy = mMap; // Shadow copy, real copy will happen on write. overridePidMapCopy = mOverridePidMap; } const size_t SIZE = 256; char buffer[SIZE]; resourceLog.append(" Processes:\n"); for (const auto& [pid, infos] : mapCopy) { snprintf(buffer, SIZE, " Pid: %d\n", pid); resourceLog.append(buffer); int priority = 0; if (getPriority_l(pid, &priority)) { snprintf(buffer, SIZE, " Priority: %d\n", priority); } else { snprintf(buffer, SIZE, " Priority: \n"); } resourceLog.append(buffer); for (const auto& [infoKey, info] : infos) { resourceLog.append(" Client:\n"); snprintf(buffer, SIZE, " Id: %lld\n", (long long)info.clientId); resourceLog.append(buffer); std::string clientName = info.name; snprintf(buffer, SIZE, " Name: %s\n", clientName.c_str()); resourceLog.append(buffer); const ResourceList& resources = info.resources; resourceLog.append(" Resources:\n"); resourceLog.append(resources.toString()); } } resourceLog.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); resourceLog.append(buffer); } } 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.c_str(), result.size()); return PERMISSION_DENIED; } bool supportsMultipleSecureCodecs; bool supportsSecureWithNonSecureCodec; String8 serviceLog; { std::scoped_lock lock{mLock}; supportsMultipleSecureCodecs = mSupportsMultipleSecureCodecs; supportsSecureWithNonSecureCodec = mSupportsSecureWithNonSecureCodec; serviceLog = mServiceLog->toString(" " /* linePrefix */); } // Get all the resource (and overload pid) log. std::string resourceLog; getResourceDump(resourceLog); // Get all the metrics log. std::string metricsLog; { std::scoped_lock lock{mLock}; metricsLog = mResourceManagerMetrics->dump(); } 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); // Add resource log. result.append(resourceLog.c_str()); // Add service log. result.append(" Events logs (most recent at top):\n"); result.append(serviceLog); // Add metrics log. result.append(metricsLog.c_str()); write(fd, result.c_str(), 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) { mSystemCB->noteResetVideo(); // Create ResourceManagerMetrics that handles all the metrics. mResourceManagerMetrics = std::make_unique(mProcessInfo); } //static void ResourceManagerService::instantiate() { std::shared_ptr service = Create(); binder_status_t status = AServiceManager_addServiceWithFlags( service->asBinder().get(), getServiceName(), AServiceManager_AddServiceFlag::ADD_SERVICE_ALLOW_ISOLATED); 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(); } std::shared_ptr ResourceManagerService::Create() { return Create(new ProcessInfo(), new SystemCallbackImpl()); } std::shared_ptr ResourceManagerService::Create( const sp& processInfo, const sp& systemResource) { std::shared_ptr service = nullptr; // If codec importance feature is on, create the refactored implementation. if (CodecFeatureFlags::codec_importance()) { service = ::ndk::SharedRefBase::make(processInfo, systemResource); } else { service = ::ndk::SharedRefBase::make(processInfo, systemResource); } if (service != nullptr) { service->init(); } return service; } // TEST only function. std::shared_ptr ResourceManagerService::CreateNew( const sp& processInfo, const sp& systemResource) { std::shared_ptr service = ::ndk::SharedRefBase::make(processInfo, systemResource); service->init(); return service; } void ResourceManagerService::init() {} 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).c_str()); mServiceLog->add(log); std::scoped_lock 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, uid_t uid) { // 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::kHwVideoCodec || resource.subType == MediaResource::SubType::kSwVideoCodec)) { mSystemCB->noteStartVideo(uid); } } void ResourceManagerService::onLastRemoved(const MediaResourceParcel& resource, uid_t uid) { 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::kHwVideoCodec || resource.subType == MediaResource::SubType::kSwVideoCodec)) { mSystemCB->noteStopVideo(uid); } } Status ResourceManagerService::addResource(const ClientInfoParcel& clientInfo, const std::shared_ptr& client, const std::vector& resources) { int32_t pid = clientInfo.pid; int32_t uid = clientInfo.uid; int64_t clientId = clientInfo.id; String8 log = String8::format("addResource(pid %d, uid %d clientId %lld, resources %s)", pid, uid, (long long) clientId, getString(resources).c_str()); mServiceLog->add(log); std::scoped_lock lock{mLock}; if (!mProcessInfo->isPidUidTrusted(pid, uid)) { pid_t callingPid = IPCThreadState::self()->getCallingPid(); uid_t callingUid = IPCThreadState::self()->getCallingUid(); ALOGW("%s called with untrusted pid %d or uid %d, using calling pid %d, uid %d", __FUNCTION__, pid, uid, callingPid, callingUid); pid = callingPid; uid = callingUid; } ResourceInfos& infos = getResourceInfosForEdit(pid, mMap); ResourceInfo& info = getResourceInfoForEdit(clientInfo, client, infos); ResourceList resourceAdded; for (size_t i = 0; i < resources.size(); ++i) { const auto &res = resources[i]; if (res.value < 0 && res.type != MediaResource::Type::kDrmSession) { ALOGW("Ignoring request to remove negative value of non-drm resource"); continue; } bool isNewEntry = false; if (!info.resources.add(res, &isNewEntry)) { continue; } if (isNewEntry) { onFirstAdded(res, info.uid); } // Add it to the list of added resources for observers. resourceAdded.add(res); } if (info.deathNotifier == nullptr && client != nullptr) { info.deathNotifier = DeathNotifier::Create( client, ref(), clientInfo); } if (mObserverService != nullptr && !resourceAdded.empty()) { mObserverService->onResourceAdded(uid, pid, resourceAdded); } notifyResourceGranted(pid, resources); return Status::ok(); } Status ResourceManagerService::removeResource(const ClientInfoParcel& clientInfo, const std::vector& resources) { int32_t pid = clientInfo.pid; int32_t uid = clientInfo.uid; int64_t clientId = clientInfo.id; String8 log = String8::format("removeResource(pid %d, uid %d clientId %lld, resources %s)", pid, uid, (long long) clientId, getString(resources).c_str()); mServiceLog->add(log); std::scoped_lock lock{mLock}; if (!mProcessInfo->isPidTrusted(pid)) { pid_t callingPid = IPCThreadState::self()->getCallingPid(); ALOGW("%s called with untrusted pid %d, using calling pid %d", __FUNCTION__, pid, callingPid); pid = callingPid; } PidResourceInfosMap::iterator found = mMap.find(pid); if (found == mMap.end()) { ALOGV("removeResource: didn't find pid %d for clientId %lld", pid, (long long) clientId); return Status::ok(); } ResourceInfos& infos = found->second; ResourceInfos::iterator foundClient = infos.find(clientId); if (foundClient == infos.end()) { ALOGV("removeResource: didn't find clientId %lld", (long long) clientId); return Status::ok(); } ResourceInfo& info = foundClient->second; ResourceList resourceRemoved; for (size_t i = 0; i < resources.size(); ++i) { const auto &res = resources[i]; if (res.value < 0) { ALOGW("Ignoring request to remove negative value of resource"); continue; } long removedEntryValue = -1; if (info.resources.remove(res, &removedEntryValue)) { MediaResourceParcel actualRemoved = res; if (removedEntryValue != -1) { onLastRemoved(res, info.uid); actualRemoved.value = removedEntryValue; } // Add it to the list of removed resources for observers. resourceRemoved.add(actualRemoved); } } if (mObserverService != nullptr && !resourceRemoved.empty()) { mObserverService->onResourceRemoved(info.uid, pid, resourceRemoved); } return Status::ok(); } Status ResourceManagerService::removeClient(const ClientInfoParcel& clientInfo) { removeResource(clientInfo, true /*checkValid*/); return Status::ok(); } Status ResourceManagerService::removeResource(const ClientInfoParcel& clientInfo, bool checkValid) { int32_t pid = clientInfo.pid; int32_t uid = clientInfo.uid; int64_t clientId = clientInfo.id; String8 log = String8::format("removeResource(pid %d, uid %d clientId %lld)", pid, uid, (long long) clientId); mServiceLog->add(log); std::scoped_lock lock{mLock}; if (checkValid && !mProcessInfo->isPidTrusted(pid)) { pid_t callingPid = IPCThreadState::self()->getCallingPid(); ALOGW("%s called with untrusted pid %d, using calling pid %d", __FUNCTION__, pid, callingPid); pid = callingPid; } PidResourceInfosMap::iterator found = mMap.find(pid); if (found == mMap.end()) { ALOGV("removeResource: didn't find pid %d for clientId %lld", pid, (long long) clientId); return Status::ok(); } ResourceInfos& infos = found->second; ResourceInfos::iterator foundClient = infos.find(clientId); if (foundClient == infos.end()) { ALOGV("removeResource: didn't find clientId %lld", (long long) clientId); return Status::ok(); } const ResourceInfo& info = foundClient->second; for (const MediaResourceParcel& res : info.resources.getResources()) { onLastRemoved(res, info.uid); } // Since this client has been removed, update the metrics collector. mResourceManagerMetrics->notifyClientReleased(clientInfo); if (mObserverService != nullptr && !info.resources.empty()) { mObserverService->onResourceRemoved(info.uid, pid, info.resources); } infos.erase(foundClient); return Status::ok(); } void ResourceManagerService::getClientForResource_l( const ResourceRequestInfo& resourceRequestInfo, std::vector& clientsInfo) { int callingPid = resourceRequestInfo.mCallingPid; const MediaResourceParcel* res = resourceRequestInfo.mResource; if (res == NULL) { return; } // Before looking into other processes, check if we have clients marked for // pending removal in the same process. ClientInfo clientInfo; if (getBiggestClientPendingRemoval_l(callingPid, res->type, res->subType, clientInfo)) { clientsInfo.emplace_back(clientInfo); return; } // Now find client(s) from a lowest priority process that has needed resources. if (getLowestPriorityBiggestClient_l(resourceRequestInfo, clientInfo)) { clientsInfo.push_back(clientInfo); } } bool ResourceManagerService::getTargetClients( const ClientInfoParcel& clientInfo, const std::vector& resources, std::vector& targetClients) { int32_t callingPid = clientInfo.pid; int64_t clientId = clientInfo.id; std::scoped_lock lock{mLock}; if (!mProcessInfo->isPidTrusted(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) { switch (resources[i].type) { case MediaResource::Type::kSecureCodec: secureCodec = &resources[i]; break; case MediaResource::Type::kNonSecureCodec: nonSecureCodec = &resources[i]; break; case MediaResource::Type::kGraphicMemory: graphicMemory = &resources[i]; break; case MediaResource::Type::kDrmSession: drmSession = &resources[i]; break; default: break; } } // first pass to handle secure/non-secure codec conflict if (secureCodec != NULL) { MediaResourceParcel mediaResource{.type = MediaResource::Type::kSecureCodec, .subType = secureCodec->subType}; ResourceRequestInfo resourceRequestInfo{callingPid, clientId, &mediaResource}; if (!mSupportsMultipleSecureCodecs) { if (!getAllClients_l(resourceRequestInfo, targetClients)) { return false; } } if (!mSupportsSecureWithNonSecureCodec) { mediaResource.type = MediaResource::Type::kNonSecureCodec; if (!getAllClients_l(resourceRequestInfo, targetClients)) { return false; } } } if (nonSecureCodec != NULL) { if (!mSupportsSecureWithNonSecureCodec) { MediaResourceParcel mediaResource{.type = MediaResource::Type::kSecureCodec, .subType = nonSecureCodec->subType}; ResourceRequestInfo resourceRequestInfo{callingPid, clientId, &mediaResource}; if (!getAllClients_l(resourceRequestInfo, targetClients)) { return false; } } } if (drmSession != NULL) { ResourceRequestInfo resourceRequestInfo{callingPid, clientId, drmSession}; getClientForResource_l(resourceRequestInfo, targetClients); if (targetClients.size() == 0) { return false; } } if (targetClients.size() == 0 && graphicMemory != nullptr) { // if no secure/non-secure codec conflict, run second pass to handle other resources. ResourceRequestInfo resourceRequestInfo{callingPid, clientId, graphicMemory}; getClientForResource_l(resourceRequestInfo, targetClients); } if (targetClients.size() == 0) { // if we are here, run the third pass to free one codec with the same type. if (secureCodec != nullptr) { ResourceRequestInfo resourceRequestInfo{callingPid, clientId, secureCodec}; getClientForResource_l(resourceRequestInfo, targetClients); } if (nonSecureCodec != nullptr) { ResourceRequestInfo resourceRequestInfo{callingPid, clientId, nonSecureCodec}; getClientForResource_l(resourceRequestInfo, targetClients); } } if (targetClients.size() == 0) { // if we are here, run the fourth pass to free one codec with the different type. if (secureCodec != nullptr) { MediaResource temp(MediaResource::Type::kNonSecureCodec, secureCodec->subType, 1); ResourceRequestInfo resourceRequestInfo{callingPid, clientId, &temp}; getClientForResource_l(resourceRequestInfo, targetClients); } if (nonSecureCodec != nullptr) { MediaResource temp(MediaResource::Type::kSecureCodec, nonSecureCodec->subType, 1); ResourceRequestInfo resourceRequestInfo{callingPid, clientId, &temp}; getClientForResource_l(resourceRequestInfo, targetClients); } } return !targetClients.empty(); } Status ResourceManagerService::reclaimResource(const ClientInfoParcel& clientInfo, const std::vector& resources, bool* _aidl_return) { std::string clientName = clientInfo.name; String8 log = String8::format("reclaimResource(callingPid %d, uid %d resources %s)", clientInfo.pid, clientInfo.uid, getString(resources).c_str()); mServiceLog->add(log); *_aidl_return = false; // Check if there are any resources to be reclaimed before processing. if (resources.empty()) { // Invalid reclaim request. So no need to log. return Status::ok(); } std::vector targetClients; if (getTargetClients(clientInfo, resources, targetClients)) { // Reclaim all the target clients. *_aidl_return = reclaimUnconditionallyFrom(targetClients); } else { // No clients to reclaim from. ALOGI("%s: There aren't any clients to reclaim from", __func__); // We need to log this failed reclaim as "no clients to reclaim from". targetClients.clear(); } // Log Reclaim Pushed Atom to statsd pushReclaimAtom(clientInfo, targetClients, *_aidl_return); return Status::ok(); } void ResourceManagerService::pushReclaimAtom(const ClientInfoParcel& clientInfo, const std::vector& targetClients, bool reclaimed) { int32_t callingPid = clientInfo.pid; int requesterPriority = -1; getPriority_l(callingPid, &requesterPriority); std::vector priorities; priorities.push_back(requesterPriority); for (const ClientInfo& targetClient : targetClients) { int targetPriority = -1; getPriority_l(targetClient.mPid, &targetPriority); priorities.push_back(targetPriority); } mResourceManagerMetrics->pushReclaimAtom(clientInfo, priorities, targetClients, reclaimed); } std::shared_ptr ResourceManagerService::getClient_l( int pid, const int64_t& clientId) const { std::map::const_iterator found = mMap.find(pid); if (found == mMap.end()) { ALOGV("%s: didn't find pid %d for clientId %lld", __func__, pid, (long long) clientId); return nullptr; } const ResourceInfos& infos = found->second; ResourceInfos::const_iterator foundClient = infos.find(clientId); if (foundClient == infos.end()) { ALOGV("%s: didn't find clientId %lld", __func__, (long long) clientId); return nullptr; } return foundClient->second.client; } bool ResourceManagerService::removeClient_l(int pid, const int64_t& clientId) { std::map::iterator found = mMap.find(pid); if (found == mMap.end()) { ALOGV("%s: didn't find pid %d for clientId %lld", __func__, pid, (long long) clientId); return false; } ResourceInfos& infos = found->second; ResourceInfos::iterator foundClient = infos.find(clientId); if (foundClient == infos.end()) { ALOGV("%s: didn't find clientId %lld", __func__, (long long) clientId); return false; } infos.erase(foundClient); return true; } bool ResourceManagerService::reclaimUnconditionallyFrom( const std::vector& targetClients) { if (targetClients.size() == 0) { return false; } int64_t failedClientId = -1; int32_t failedClientPid = -1; for (const ClientInfo& targetClient : targetClients) { std::shared_ptr client = nullptr; { std::scoped_lock lock{mLock}; client = getClient_l(targetClient.mPid, targetClient.mClientId); } if (client == nullptr) { // skip already released clients. continue; } String8 log = String8::format("reclaimResource from client %p", client.get()); mServiceLog->add(log); bool success; Status status = client->reclaimResource(&success); if (!status.isOk() || !success) { failedClientId = targetClient.mClientId; failedClientPid = targetClient.mPid; break; } } if (failedClientId == -1) { return true; } { std::scoped_lock lock{mLock}; bool found = removeClient_l(failedClientPid, failedClientId); if (found) { ALOGW("Failed to reclaim resources from client with pid %d", failedClientPid); } else { ALOGW("Failed to reclaim resources from unlocateable client"); } } return false; } bool ResourceManagerService::overridePid_l(int32_t originalPid, int32_t newPid) { mOverridePidMap.erase(originalPid); if (newPid != -1) { mOverridePidMap.emplace(originalPid, newPid); return true; } 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); } { std::scoped_lock lock{mLock}; if (overridePid_l(originalPid, newPid)) { mResourceManagerMetrics->addPid(newPid); } } return Status::ok(); } bool ResourceManagerService::overrideProcessInfo_l( const std::shared_ptr& client, int pid, int procState, int oomScore) { removeProcessInfoOverride_l(pid); if (!mProcessInfo->overrideProcessInfo(pid, procState, oomScore)) { // Override value is rejected by ProcessInfo. return false; } ClientInfoParcel clientInfo{.pid = static_cast(pid), .uid = 0, .id = 0, .name = ""}; auto deathNotifier = DeathNotifier::Create( client, ref(), clientInfo, true); mProcessInfoOverrideMap.emplace(pid, ProcessInfoOverride{deathNotifier, client}); return true; } 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); } std::scoped_lock lock{mLock}; if (!overrideProcessInfo_l(client, pid, procState, oomScore)) { // Override value is rejected by ProcessInfo. return Status::fromServiceSpecificError(BAD_VALUE); } return Status::ok(); } void ResourceManagerService::removeProcessInfoOverride(int pid) { std::scoped_lock lock{mLock}; removeProcessInfoOverride_l(pid); } void ResourceManagerService::removeProcessInfoOverride_l(int pid) { auto it = mProcessInfoOverrideMap.find(pid); if (it == mProcessInfoOverrideMap.end()) { return; } mProcessInfo->removeProcessInfoOverride(pid); mProcessInfoOverrideMap.erase(pid); } Status ResourceManagerService::markClientForPendingRemoval(const ClientInfoParcel& clientInfo) { int32_t pid = clientInfo.pid; int64_t clientId = clientInfo.id; String8 log = String8::format( "markClientForPendingRemoval(pid %d, clientId %lld)", pid, (long long) clientId); mServiceLog->add(log); std::scoped_lock lock{mLock}; if (!mProcessInfo->isPidTrusted(pid)) { pid_t callingPid = IPCThreadState::self()->getCallingPid(); ALOGW("%s called with untrusted pid %d, using calling pid %d", __FUNCTION__, pid, callingPid); pid = callingPid; } PidResourceInfosMap::iterator found = mMap.find(pid); if (found == mMap.end()) { ALOGV("markClientForPendingRemoval: didn't find pid %d for clientId %lld", pid, (long long)clientId); return Status::ok(); } ResourceInfos& infos = found->second; ResourceInfos::iterator foundClient = infos.find(clientId); if (foundClient == infos.end()) { ALOGV("markClientForPendingRemoval: didn't find clientId %lld", (long long) clientId); return Status::ok(); } ResourceInfo& info = foundClient->second; info.pendingRemoval = true; return Status::ok(); } Status ResourceManagerService::reclaimResourcesFromClientsPendingRemoval(int32_t pid) { String8 log = String8::format("reclaimResourcesFromClientsPendingRemoval(pid %d)", pid); mServiceLog->add(log); std::vector targetClients; { std::scoped_lock lock{mLock}; if (!mProcessInfo->isPidTrusted(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}) { switch (type) { // Codec resources are segregated by audio, video and image domains. case MediaResource::Type::kSecureCodec: case MediaResource::Type::kNonSecureCodec: for (MediaResource::SubType subType : {MediaResource::SubType::kHwAudioCodec, MediaResource::SubType::kSwAudioCodec, MediaResource::SubType::kHwVideoCodec, MediaResource::SubType::kSwVideoCodec, MediaResource::SubType::kHwImageCodec, MediaResource::SubType::kSwImageCodec}) { ClientInfo clientInfo; if (getBiggestClientPendingRemoval_l(pid, type, subType, clientInfo)) { targetClients.emplace_back(clientInfo); continue; } } break; // Non-codec resources are shared by audio, video and image codecs (no subtype). default: ClientInfo clientInfo; if (getBiggestClientPendingRemoval_l(pid, type, MediaResource::SubType::kUnspecifiedSubType, clientInfo)) { targetClients.emplace_back(clientInfo); } break; } } } if (!targetClients.empty()) { reclaimUnconditionallyFrom(targetClients); } return Status::ok(); } bool ResourceManagerService::getPriority_l(int pid, int* priority) const { int newPid = pid; std::map::const_iterator found = mOverridePidMap.find(pid); if (found != mOverridePidMap.end()) { newPid = found->second; ALOGD("getPriority_l: use override pid %d instead original pid %d", newPid, pid); } return mProcessInfo->getPriority(newPid, priority); } bool ResourceManagerService::getAllClients_l( const ResourceRequestInfo& resourceRequestInfo, std::vector& clientsInfo) { MediaResource::Type type = resourceRequestInfo.mResource->type; MediaResource::SubType subType = resourceRequestInfo.mResource->subType; for (auto& [pid, infos] : mMap) { for (const auto& [id, info] : infos) { if (pid == resourceRequestInfo.mCallingPid && id == resourceRequestInfo.mClientId) { ALOGI("%s: Skip the client[%jd] for which the resource request is made", __func__, id); continue; } if (hasResourceType(type, subType, info.resources)) { if (!isCallingPriorityHigher_l(resourceRequestInfo.mCallingPid, pid)) { // some higher/equal priority process owns the resource, // this request can't be fulfilled. ALOGE("%s: can't reclaim resource %s from pid %d", __func__, asString(type), pid); clientsInfo.clear(); return false; } clientsInfo.emplace_back(pid, info.uid, info.clientId); } } } if (clientsInfo.size() == 0) { ALOGV("%s: didn't find any resource %s", __func__, asString(type)); } return true; } // Process priority (oom score) based reclaim: // - Find a process with lowest priority (than that of calling process). // - Find the bigegst client (with required resources) from that process. bool ResourceManagerService::getLowestPriorityBiggestClient_l( const ResourceRequestInfo& resourceRequestInfo, ClientInfo& clientInfo) { int callingPid = resourceRequestInfo.mCallingPid; MediaResource::Type type = resourceRequestInfo.mResource->type; MediaResource::SubType subType = resourceRequestInfo.mResource->subType; int lowestPriorityPid; int lowestPriority; int callingPriority; if (!getPriority_l(callingPid, &callingPriority)) { ALOGE("%s: can't get process priority for pid %d", __func__, callingPid); return false; } if (!getLowestPriorityPid_l(type, subType, &lowestPriorityPid, &lowestPriority)) { return false; } if (lowestPriority <= callingPriority) { ALOGE("%s: lowest priority %d vs caller priority %d", __func__, lowestPriority, callingPriority); return false; } if (!getBiggestClient_l(lowestPriorityPid, type, subType, clientInfo)) { return false; } ALOGI("%s: CallingProcess(%d:%d) will reclaim from the lowestPriorityProcess(%d:%d)", __func__, callingPid, callingPriority, lowestPriorityPid, lowestPriority); return true; } bool ResourceManagerService::getLowestPriorityPid_l(MediaResource::Type type, MediaResource::SubType subType, int *lowestPriorityPid, int *lowestPriority) { int pid = -1; int priority = -1; for (auto& [tempPid, infos] : mMap) { if (infos.size() == 0) { // no client on this process. continue; } if (!hasResourceType(type, subType, infos)) { // doesn't have the requested resource type continue; } int tempPriority = -1; 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::getBiggestClientPendingRemoval_l(int pid, MediaResource::Type type, MediaResource::SubType subType, ClientInfo& clientInfo) { return getBiggestClient_l(pid, type, subType, clientInfo, true /* pendingRemovalOnly */); } bool ResourceManagerService::getBiggestClient_l(int pid, MediaResource::Type type, MediaResource::SubType subType, ClientInfo& clientInfo, bool pendingRemovalOnly) { PidResourceInfosMap::iterator found = mMap.find(pid); if (found == mMap.end()) { ALOGE_IF(!pendingRemovalOnly, "getBiggestClient_l: can't find resource info for pid %d", pid); return false; } uid_t uid = -1; int64_t clientId = -1; uint64_t largestValue = 0; const ResourceInfos& infos = found->second; for (const auto& [id, info] : infos) { const ResourceList& resources = info.resources; if (pendingRemovalOnly && !info.pendingRemoval) { continue; } for (const MediaResourceParcel& resource : resources.getResources()) { if (hasResourceType(type, subType, resource)) { if (resource.value > largestValue) { largestValue = resource.value; clientId = info.clientId; uid = info.uid; } } } } if (clientId == -1) { ALOGE_IF(!pendingRemovalOnly, "getBiggestClient_l: can't find resource type %s and subtype %s for pid %d", asString(type), asString(subType), pid); return false; } clientInfo.mPid = pid; clientInfo.mUid = uid; clientInfo.mClientId = clientId; return true; } Status ResourceManagerService::notifyClientCreated(const ClientInfoParcel& clientInfo) { mResourceManagerMetrics->notifyClientCreated(clientInfo); return Status::ok(); } Status ResourceManagerService::notifyClientStarted(const ClientConfigParcel& clientConfig) { mResourceManagerMetrics->notifyClientStarted(clientConfig); return Status::ok(); } Status ResourceManagerService::notifyClientStopped(const ClientConfigParcel& clientConfig) { mResourceManagerMetrics->notifyClientStopped(clientConfig); return Status::ok(); } Status ResourceManagerService::notifyClientConfigChanged(const ClientConfigParcel& clientConfig) { mResourceManagerMetrics->notifyClientConfigChanged(clientConfig); return Status::ok(); } long ResourceManagerService::getPeakConcurrentPixelCount(int pid) const { return mResourceManagerMetrics->getPeakConcurrentPixelCount(pid); } long ResourceManagerService::getCurrentConcurrentPixelCount(int pid) const { return mResourceManagerMetrics->getCurrentConcurrentPixelCount(pid); } void ResourceManagerService::notifyClientReleased(const ClientInfoParcel& clientInfo) { mResourceManagerMetrics->notifyClientReleased(clientInfo); } } // namespace android