resourcemanager: refactor resource map into an abstraction
This CL moves the resource tracking into a new abstraction
ResourceTracker by moving all the related codec from the
ResourceManagerServiceNew into this new class.
Bug: 294886363
Test: atest android.media.misc.cts.ResourceManagerTest
atest android.media.misc.cts.ResourceManagerMultiTest
/data/nativetest64/ResourceManagerService_test/ResourceManagerService_test
/data/nativetest64/ResourceObserverService_test/ResourceObserverService_test
/data/nativetest64/ServiceLog_test/ServiceLog_test
Change-Id: I0ac5739cbefff3756cc3a9f20a6bc6c0ddd9e732
diff --git a/services/mediaresourcemanager/ResourceTracker.cpp b/services/mediaresourcemanager/ResourceTracker.cpp
new file mode 100644
index 0000000..c6255a2
--- /dev/null
+++ b/services/mediaresourcemanager/ResourceTracker.cpp
@@ -0,0 +1,564 @@
+/*
+**
+** 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 <utils/Log.h>
+
+#include <binder/IPCThreadState.h>
+#include <mediautils/ProcessInfo.h>
+#include "ResourceTracker.h"
+#include "ResourceManagerServiceNew.h"
+#include "ResourceObserverService.h"
+
+namespace android {
+
+ResourceTracker::ResourceTracker(const std::shared_ptr<ResourceManagerServiceNew>& service,
+ const sp<ProcessInfoInterface>& processInfo) :
+ mService(service),
+ mProcessInfo(processInfo) {
+}
+
+ResourceTracker::~ResourceTracker() {
+}
+
+void ResourceTracker::setResourceObserverService(
+ const std::shared_ptr<ResourceObserverService>& observerService) {
+ mObserverService = observerService;
+}
+
+ResourceInfos& ResourceTracker::getResourceInfosForEdit(int pid) {
+ std::map<int, ResourceInfos>::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<IResourceManagerClient>& client,
+ const std::vector<MediaResourceParcel>& 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) {
+ const auto resType = std::tuple(res.type, res.subType, res.id);
+
+ if (res.value < 0 && res.type != MediaResource::Type::kDrmSession) {
+ ALOGV("%s: Ignoring request to remove negative value of non-drm resource", __func__);
+ 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.
+ ALOGV("%s: Ignoring request to add new resource entry with value <= 0", __func__);
+ continue;
+ }
+ onFirstAdded(res, info.uid);
+ 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.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::removeResource(const ClientInfoParcel& clientInfo,
+ const std::vector<MediaResourceParcel>& 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<int, ResourceInfos>::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) {
+ const auto resType = std::tuple(res.type, res.subType, res.id);
+
+ if (res.value < 0) {
+ ALOGV("%s: Ignoring request to remove negative value of resource", __func__);
+ 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.uid);
+ 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 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<int, ResourceInfos>::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 (auto& [resType, resParcel] : info.resources) {
+ onLastRemoved(resParcel, info.uid);
+ }
+
+ if (mObserverService != nullptr && !info.resources.empty()) {
+ mObserverService->onResourceRemoved(info.uid, pid, info.resources);
+ }
+
+ infos.erase(foundClient);
+ return true;
+}
+
+std::shared_ptr<IResourceManagerClient> ResourceTracker::getClient(
+ int pid, const int64_t& clientId) const {
+ std::map<int, ResourceInfos>::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<int, ResourceInfos>::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<int, ResourceInfos>::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<ClientInfo>& 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 (getBiggestClient(pid, type, subType, clientInfo, true)) {
+ 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 (getBiggestClient(pid, type, subType, clientInfo, true)) {
+ 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<IResourceManagerClient>& 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<int32_t>(pid),
+ .uid = 0,
+ .id = 0,
+ .name = "<unknown client>"};
+ std::shared_ptr<DeathNotifier> 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<ClientInfo>& clients) {
+ 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)) {
+ 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::getBiggestClient(int pid, MediaResource::Type type,
+ MediaResource::SubType subType,
+ ClientInfo& clientInfo, bool pendingRemovalOnly) {
+ std::map<int, ResourceInfos>::iterator found = mMap.find(pid);
+ if (found == mMap.end()) {
+ ALOGE_IF(!pendingRemovalOnly, "%s: can't find resource info for pid %d", __func__, pid);
+ 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;
+ if (pendingRemovalOnly && !info.pendingRemoval) {
+ continue;
+ }
+ for (auto it = resources.begin(); it != resources.end(); it++) {
+ const MediaResourceParcel &resource = it->second;
+ if (hasResourceType(type, subType, resource)) {
+ if (resource.value > largestValue) {
+ largestValue = resource.value;
+ clientId = info.clientId;
+ uid = info.uid;
+ }
+ }
+ }
+ }
+
+ if (clientId == -1) {
+ ALOGE_IF(!pendingRemovalOnly,
+ "%s: can't find resource type %s and subtype %s for pid %d",
+ __func__, asString(type), asString(subType), pid);
+ return false;
+ }
+
+ clientInfo.mPid = pid;
+ 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: <unknown>\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");
+ for (auto it = resources.begin(); it != resources.end(); it++) {
+ snprintf(buffer, SIZE, " %s\n", toString(it->second).c_str());
+ resourceLogs.append(buffer);
+ }
+ }
+ }
+ 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<ResourceManagerServiceNew> 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<ResourceManagerServiceNew> 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<ClientInfo>& 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 (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 {
+ clients.emplace_back(info.pid, info.uid, info.clientId);
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+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