/* ** ** Copyright 2023, 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 "ResourceTracker" #include #include #include #include "ResourceTracker.h" #include "ResourceManagerServiceNew.h" #include "ResourceObserverService.h" namespace android { inline bool isHwCodec(MediaResource::SubType subType) { return subType == MediaResource::SubType::kHwImageCodec || subType == MediaResource::SubType::kHwVideoCodec; } // Check whether a given resource (of type and subtype) is found in given resource list // that also has the given Primary SubType. static bool hasResourceType(MediaResource::Type type, MediaResource::SubType subType, const ResourceList& resources, MediaResource::SubType primarySubType) { bool foundResource = false; bool matchedPrimary = (primarySubType == MediaResource::SubType::kUnspecifiedSubType) ? true : false; for (const MediaResourceParcel& res : resources.getResources()) { if (hasResourceType(type, subType, res)) { foundResource = true; } else if (res.subType == primarySubType) { matchedPrimary = true; } else if (isHwCodec(res.subType) == isHwCodec(primarySubType)) { matchedPrimary = true; } if (matchedPrimary && foundResource) { return true; } } return false; } // See if the given client is already in the list of clients. inline bool contains(const std::vector& clients, const int64_t& clientId) { std::vector::const_iterator found = std::find_if(clients.begin(), clients.end(), [clientId](const ClientInfo& client) -> bool { return client.mClientId == clientId; }); return found != clients.end(); } ResourceTracker::ResourceTracker(const std::shared_ptr& service, const sp& processInfo) : mService(service), mProcessInfo(processInfo) { } ResourceTracker::~ResourceTracker() { } void ResourceTracker::setResourceObserverService( const std::shared_ptr& observerService) { mObserverService = observerService; } ResourceInfos& ResourceTracker::getResourceInfosForEdit(int pid) { std::map::iterator found = mMap.find(pid); if (found == mMap.end()) { // new pid ResourceInfos infosForPid; auto [it, inserted] = mMap.emplace(pid, infosForPid); found = it; } return found->second; } bool ResourceTracker::addResource(const ClientInfoParcel& clientInfo, const std::shared_ptr& client, const std::vector& resources) { int32_t pid = clientInfo.pid; int32_t uid = clientInfo.uid; 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", __func__, pid, uid, callingPid, callingUid); pid = callingPid; uid = callingUid; } ResourceInfos& infos = getResourceInfosForEdit(pid); ResourceInfo& info = getResourceInfoForEdit(clientInfo, client, infos); ResourceList resourceAdded; for (const MediaResourceParcel& res : resources) { if (res.value < 0 && res.type != MediaResource::Type::kDrmSession) { ALOGV("%s: Ignoring request to remove negative value of non-drm resource", __func__); 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, mService, clientInfo); } if (mObserverService != nullptr && !resourceAdded.empty()) { mObserverService->onResourceAdded(uid, pid, resourceAdded); } return !resourceAdded.empty(); } bool ResourceTracker::updateResource(const aidl::android::media::ClientInfoParcel& clientInfo) { ResourceInfos& infos = getResourceInfosForEdit(clientInfo.pid); ResourceInfos::iterator found = infos.find(clientInfo.id); if (found == infos.end()) { return false; } // Update the client importance. found->second.importance = std::max(0, clientInfo.importance); return true; } bool ResourceTracker::removeResource(const ClientInfoParcel& clientInfo, const std::vector& resources) { int32_t pid = clientInfo.pid; int64_t clientId = clientInfo.id; if (!mProcessInfo->isPidTrusted(pid)) { pid_t callingPid = IPCThreadState::self()->getCallingPid(); ALOGW("%s called with untrusted pid %d, using calling pid %d", __func__, pid, callingPid); pid = callingPid; } 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; } ResourceInfo& info = foundClient->second; ResourceList resourceRemoved; for (const MediaResourceParcel& res : resources) { if (res.value < 0) { ALOGV("%s: Ignoring request to remove negative value of resource", __func__); 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 true; } bool ResourceTracker::removeResource(const ClientInfoParcel& clientInfo, bool validateCallingPid) { int32_t pid = clientInfo.pid; int64_t clientId = clientInfo.id; if (validateCallingPid && !mProcessInfo->isPidTrusted(pid)) { pid_t callingPid = IPCThreadState::self()->getCallingPid(); ALOGW("%s called with untrusted pid %d, using calling pid %d", __func__, pid, callingPid); pid = callingPid; } 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; } const ResourceInfo& info = foundClient->second; for (const MediaResourceParcel& res : info.resources.getResources()) { onLastRemoved(res, info.uid); } if (mObserverService != nullptr && !info.resources.empty()) { mObserverService->onResourceRemoved(info.uid, pid, info.resources); } infos.erase(foundClient); return true; } std::shared_ptr ResourceTracker::getClient( 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 ResourceTracker::removeClient(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 ResourceTracker::markClientForPendingRemoval(const ClientInfoParcel& clientInfo) { int32_t pid = clientInfo.pid; int64_t clientId = clientInfo.id; if (!mProcessInfo->isPidTrusted(pid)) { pid_t callingPid = IPCThreadState::self()->getCallingPid(); ALOGW("%s called with untrusted pid %d, using calling pid %d", __func__, pid, callingPid); pid = callingPid; } 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; } ResourceInfo& info = foundClient->second; info.pendingRemoval = true; return true; } bool ResourceTracker::getClientsMarkedPendingRemoval(int32_t pid, std::vector& targetClients) { if (!mProcessInfo->isPidTrusted(pid)) { pid_t callingPid = IPCThreadState::self()->getCallingPid(); ALOGW("%s called with untrusted pid %d, using calling pid %d", __func__, pid, callingPid); pid = callingPid; } // Go through all the MediaResource types (and corresponding subtypes for // each, if applicable) and see if the process (with given pid) holds any // such resources that are marked as pending removal. // Since the use-case of this function is to get all such resources (pending // removal) and reclaim them all - the order in which we look for the // resource type doesn't matter. 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(pid, type, subType, clientInfo)) { if (!contains(targetClients, clientInfo.mClientId)) { targetClients.emplace_back(clientInfo); } continue; } } break; // Non-codec resources are shared by audio, video and image codecs (no subtype). default: ClientInfo clientInfo; MediaResource::SubType subType = MediaResource::SubType::kUnspecifiedSubType; if (getBiggestClientPendingRemoval(pid, type, subType, clientInfo)) { if (!contains(targetClients, clientInfo.mClientId)) { targetClients.emplace_back(clientInfo); } } break; } } return true; } bool ResourceTracker::overridePid(int originalPid, int newPid) { mOverridePidMap.erase(originalPid); if (newPid != -1) { mOverridePidMap.emplace(originalPid, newPid); return true; } return false; } bool ResourceTracker::overrideProcessInfo(const std::shared_ptr& client, int pid, int procState, int oomScore) { removeProcessInfoOverride(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 = ""}; std::shared_ptr deathNotifier = DeathNotifier::Create(client, mService, clientInfo, true); mProcessInfoOverrideMap.emplace(pid, ProcessInfoOverride{deathNotifier, client}); return true; } void ResourceTracker::removeProcessInfoOverride(int pid) { auto it = mProcessInfoOverrideMap.find(pid); if (it == mProcessInfoOverrideMap.end()) { return; } mProcessInfo->removeProcessInfoOverride(pid); mProcessInfoOverrideMap.erase(pid); } bool ResourceTracker::getAllClients(const ResourceRequestInfo& resourceRequestInfo, std::vector& clients, MediaResource::SubType primarySubType) { MediaResource::Type type = resourceRequestInfo.mResource->type; MediaResource::SubType subType = resourceRequestInfo.mResource->subType; bool foundClient = false; for (auto& [pid, /* ResourceInfos */ infos] : mMap) { for (auto& [id, /* ResourceInfo */ info] : infos) { if (hasResourceType(type, subType, info.resources, primarySubType)) { if (!contains(clients, info.clientId)) { clients.emplace_back(info.pid, info.uid, info.clientId); foundClient = true; } } } } return foundClient; } bool ResourceTracker::getLowestPriorityPid(MediaResource::Type type, MediaResource::SubType subType, int& lowestPriorityPid, int& lowestPriority) { int pid = -1; int priority = -1; for (auto& [tempPid, /* ResourceInfos */ 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(tempPid, &tempPriority)) { ALOGV("%s: can't get priority of pid %d, skipped", __func__, tempPid); // TODO: remove this pid from mMap? continue; } if (pid == -1 || tempPriority > priority) { // initial the value pid = tempPid; priority = tempPriority; } } bool success = (pid != -1); if (success) { lowestPriorityPid = pid; lowestPriority = priority; } return success; } bool ResourceTracker::getLowestPriorityPid(MediaResource::Type type, MediaResource::SubType subType, MediaResource::SubType primarySubType, const std::vector& clients, int& lowestPriorityPid, int& lowestPriority) { int pid = -1; int priority = -1; for (const ClientInfo& client : clients) { const ResourceInfo* info = getResourceInfo(client.mPid, client.mClientId); if (info == nullptr) { continue; } if (!hasResourceType(type, subType, info->resources, primarySubType)) { // doesn't have the requested resource type continue; } int tempPriority = -1; if (!getPriority(client.mPid, &tempPriority)) { ALOGV("%s: can't get priority of pid %d, skipped", __func__, client.mPid); // TODO: remove this pid from mMap? continue; } if (pid == -1 || tempPriority > priority) { // initial the value pid = client.mPid; priority = tempPriority; } } bool success = (pid != -1); if (success) { lowestPriorityPid = pid; lowestPriority = priority; } return success; } bool ResourceTracker::getBiggestClientPendingRemoval(int pid, MediaResource::Type type, MediaResource::SubType subType, ClientInfo& clientInfo) { std::map::iterator found = mMap.find(pid); if (found == mMap.end()) { return false; } uid_t uid = -1; int64_t clientId = -1; uint64_t largestValue = 0; const ResourceInfos& infos = found->second; for (const auto& [id, /* ResourceInfo */ info] : infos) { const ResourceList& resources = info.resources; // Skip if the client is not marked pending removal. if (!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) { return false; } clientInfo.mPid = pid; clientInfo.mUid = uid; clientInfo.mClientId = clientId; return true; } bool ResourceTracker::getBiggestClient(int targetPid, MediaResource::Type type, MediaResource::SubType subType, const std::vector& clients, ClientInfo& clientInfo, MediaResource::SubType primarySubType) { uid_t uid = -1; int64_t clientId = -1; uint64_t largestValue = 0; for (const ClientInfo& client : clients) { // Skip the clients that doesn't belong go the targetPid if (client.mPid != targetPid) { continue; } const ResourceInfo* info = getResourceInfo(client.mPid, client.mClientId); if (info == nullptr) { continue; } const ResourceList& resources = info->resources; bool matchedPrimary = (primarySubType == MediaResource::SubType::kUnspecifiedSubType) ? true : false; for (const MediaResourceParcel& resource : resources.getResources()) { if (resource.subType == primarySubType) { matchedPrimary = true; break; } else if (isHwCodec(resource.subType) == isHwCodec(primarySubType)) { matchedPrimary = true; break; } } // Primary type doesn't match, skip the client if (!matchedPrimary) { 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("%s: can't find resource type %s and subtype %s for pid %d", __func__, asString(type), asString(subType), targetPid); return false; } clientInfo.mPid = targetPid; clientInfo.mUid = uid; clientInfo.mClientId = clientId; return true; } bool ResourceTracker::getLeastImportantBiggestClient(int targetPid, int32_t importance, MediaResource::Type type, MediaResource::SubType subType, MediaResource::SubType primarySubType, const std::vector& clients, ClientInfo& clientInfo) { uid_t uid = -1; int64_t clientId = -1; uint64_t largestValue = 0; for (const ClientInfo& client : clients) { // Skip the clients that doesn't belong go the targetPid if (client.mPid != targetPid) { continue; } const ResourceInfo* info = getResourceInfo(client.mPid, client.mClientId); if (info == nullptr) { continue; } // Make sure the importance is lower. if (info->importance <= importance) { continue; } const ResourceList& resources = info->resources; bool matchedPrimary = (primarySubType == MediaResource::SubType::kUnspecifiedSubType) ? true : false; for (const MediaResourceParcel& resource : resources.getResources()) { if (resource.subType == primarySubType) { matchedPrimary = true; } else if (isHwCodec(resource.subType) == isHwCodec(primarySubType)) { matchedPrimary = true; } } // Primary type doesn't match, skip the client if (!matchedPrimary) { 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("%s: can't find resource type %s and subtype %s for pid %d", __func__, asString(type), asString(subType), targetPid); return false; } clientInfo.mPid = targetPid; clientInfo.mUid = uid; clientInfo.mClientId = clientId; return true; } void ResourceTracker::dump(std::string& resourceLogs) { const size_t SIZE = 256; char buffer[SIZE]; resourceLogs.append(" Processes:\n"); for (const auto& [pid, /* ResourceInfos */ infos] : mMap) { snprintf(buffer, SIZE, " Pid: %d\n", pid); resourceLogs.append(buffer); int priority = 0; if (getPriority(pid, &priority)) { snprintf(buffer, SIZE, " Priority: %d\n", priority); } else { snprintf(buffer, SIZE, " Priority: \n"); } resourceLogs.append(buffer); for (const auto& [infoKey, /* ResourceInfo */ info] : infos) { resourceLogs.append(" Client:\n"); snprintf(buffer, SIZE, " Id: %lld\n", (long long)info.clientId); resourceLogs.append(buffer); std::string clientName = info.name; snprintf(buffer, SIZE, " Name: %s\n", clientName.c_str()); resourceLogs.append(buffer); const ResourceList& resources = info.resources; resourceLogs.append(" Resources:\n"); resourceLogs.append(resources.toString()); } } resourceLogs.append(" Process Pid override:\n"); for (const auto& [oldPid, newPid] : mOverridePidMap) { snprintf(buffer, SIZE, " Original Pid: %d, Override Pid: %d\n", oldPid, newPid); resourceLogs.append(buffer); } } void ResourceTracker::onFirstAdded(const MediaResourceParcel& resource, uid_t uid) { std::shared_ptr service = mService.lock(); if (service == nullptr) { ALOGW("%s: ResourceManagerService is invalid!", __func__); return; } service->onFirstAdded(resource, uid); } void ResourceTracker::onLastRemoved(const MediaResourceParcel& resource, uid_t uid) { std::shared_ptr service = mService.lock(); if (service == nullptr) { ALOGW("%s: ResourceManagerService is invalid!", __func__); return; } service->onLastRemoved(resource, uid); } bool ResourceTracker::getPriority(int pid, int* priority) { int newPid = pid; if (mOverridePidMap.find(pid) != mOverridePidMap.end()) { newPid = mOverridePidMap[pid]; ALOGD("%s: use override pid %d instead original pid %d", __func__, newPid, pid); } return mProcessInfo->getPriority(newPid, priority); } bool ResourceTracker::getNonConflictingClients(const ResourceRequestInfo& resourceRequestInfo, std::vector& clients) { MediaResource::Type type = resourceRequestInfo.mResource->type; MediaResource::SubType subType = resourceRequestInfo.mResource->subType; for (auto& [pid, /* ResourceInfos */ infos] : mMap) { for (const auto& [id, /* ResourceInfo */ 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(resourceRequestInfo.mCallingPid, pid)) { // some higher/equal priority process owns the resource, // this is a conflict. ALOGE("%s: The resource (%s) request from pid %d is conflicting", __func__, asString(type), pid); clients.clear(); return false; } else { if (!contains(clients, info.clientId)) { clients.emplace_back(info.pid, info.uid, info.clientId); } } } } } return true; } const ResourceInfo* ResourceTracker::getResourceInfo(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; } bool ResourceTracker::isCallingPriorityHigher(int callingPid, int pid) { int callingPidPriority; if (!getPriority(callingPid, &callingPidPriority)) { return false; } int priority; if (!getPriority(pid, &priority)) { return false; } return (callingPidPriority < priority); } } // namespace android