blob: 3ee20cd39ea92014d5fab3bc74f6501457e4e906 [file] [log] [blame]
/*
**
** 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 {
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<ClientInfo>& clients, const int64_t& clientId) {
std::vector<ClientInfo>::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<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) {
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<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) {
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<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 (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<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 (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<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::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<ClientInfo>& 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<int, ResourceInfos>::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<ClientInfo>& 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<ClientInfo>& 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: <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");
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<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 (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<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;
}
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