blob: c6255a2ce397f4c3385320f85014b6305456e450 [file] [log] [blame]
Girish6a6044d2023-11-22 21:23:14 +00001/*
2**
3** Copyright 2023, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9** http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
18//#define LOG_NDEBUG 0
19#define LOG_TAG "ResourceTracker"
20#include <utils/Log.h>
21
22#include <binder/IPCThreadState.h>
23#include <mediautils/ProcessInfo.h>
24#include "ResourceTracker.h"
25#include "ResourceManagerServiceNew.h"
26#include "ResourceObserverService.h"
27
28namespace android {
29
30ResourceTracker::ResourceTracker(const std::shared_ptr<ResourceManagerServiceNew>& service,
31 const sp<ProcessInfoInterface>& processInfo) :
32 mService(service),
33 mProcessInfo(processInfo) {
34}
35
36ResourceTracker::~ResourceTracker() {
37}
38
39void ResourceTracker::setResourceObserverService(
40 const std::shared_ptr<ResourceObserverService>& observerService) {
41 mObserverService = observerService;
42}
43
44ResourceInfos& ResourceTracker::getResourceInfosForEdit(int pid) {
45 std::map<int, ResourceInfos>::iterator found = mMap.find(pid);
46 if (found == mMap.end()) {
47 // new pid
48 ResourceInfos infosForPid;
49 auto [it, inserted] = mMap.emplace(pid, infosForPid);
50 found = it;
51 }
52
53 return found->second;
54}
55
56bool ResourceTracker::addResource(const ClientInfoParcel& clientInfo,
57 const std::shared_ptr<IResourceManagerClient>& client,
58 const std::vector<MediaResourceParcel>& resources) {
59 int32_t pid = clientInfo.pid;
60 int32_t uid = clientInfo.uid;
61
62 if (!mProcessInfo->isPidUidTrusted(pid, uid)) {
63 pid_t callingPid = IPCThreadState::self()->getCallingPid();
64 uid_t callingUid = IPCThreadState::self()->getCallingUid();
65 ALOGW("%s called with untrusted pid %d or uid %d, using calling pid %d, uid %d",
66 __func__, pid, uid, callingPid, callingUid);
67 pid = callingPid;
68 uid = callingUid;
69 }
70 ResourceInfos& infos = getResourceInfosForEdit(pid);
71 ResourceInfo& info = getResourceInfoForEdit(clientInfo, client, infos);
72 ResourceList resourceAdded;
73
74 for (const MediaResourceParcel& res : resources) {
75 const auto resType = std::tuple(res.type, res.subType, res.id);
76
77 if (res.value < 0 && res.type != MediaResource::Type::kDrmSession) {
78 ALOGV("%s: Ignoring request to remove negative value of non-drm resource", __func__);
79 continue;
80 }
81 if (info.resources.find(resType) == info.resources.end()) {
82 if (res.value <= 0) {
83 // We can't init a new entry with negative value, although it's allowed
84 // to merge in negative values after the initial add.
85 ALOGV("%s: Ignoring request to add new resource entry with value <= 0", __func__);
86 continue;
87 }
88 onFirstAdded(res, info.uid);
89 info.resources[resType] = res;
90 } else {
91 mergeResources(info.resources[resType], res);
92 }
93 // Add it to the list of added resources for observers.
94 auto it = resourceAdded.find(resType);
95 if (it == resourceAdded.end()) {
96 resourceAdded[resType] = res;
97 } else {
98 mergeResources(it->second, res);
99 }
100 }
101 if (info.deathNotifier == nullptr && client != nullptr) {
102 info.deathNotifier = DeathNotifier::Create(client, mService, clientInfo);
103 }
104 if (mObserverService != nullptr && !resourceAdded.empty()) {
105 mObserverService->onResourceAdded(uid, pid, resourceAdded);
106 }
107
108 return !resourceAdded.empty();
109}
110
111bool ResourceTracker::removeResource(const ClientInfoParcel& clientInfo,
112 const std::vector<MediaResourceParcel>& resources) {
113 int32_t pid = clientInfo.pid;
114 int64_t clientId = clientInfo.id;
115
116 if (!mProcessInfo->isPidTrusted(pid)) {
117 pid_t callingPid = IPCThreadState::self()->getCallingPid();
118 ALOGW("%s called with untrusted pid %d, using calling pid %d", __func__,
119 pid, callingPid);
120 pid = callingPid;
121 }
122 std::map<int, ResourceInfos>::iterator found = mMap.find(pid);
123 if (found == mMap.end()) {
124 ALOGV("%s: didn't find pid %d for clientId %lld", __func__, pid, (long long) clientId);
125 return false;
126 }
127
128 ResourceInfos& infos = found->second;
129 ResourceInfos::iterator foundClient = infos.find(clientId);
130 if (foundClient == infos.end()) {
131 ALOGV("%s: didn't find clientId %lld", __func__, (long long) clientId);
132 return false;
133 }
134
135 ResourceInfo& info = foundClient->second;
136 ResourceList resourceRemoved;
137 for (const MediaResourceParcel& res : resources) {
138 const auto resType = std::tuple(res.type, res.subType, res.id);
139
140 if (res.value < 0) {
141 ALOGV("%s: Ignoring request to remove negative value of resource", __func__);
142 continue;
143 }
144 // ignore if we don't have it
145 if (info.resources.find(resType) != info.resources.end()) {
146 MediaResourceParcel& resource = info.resources[resType];
147 MediaResourceParcel actualRemoved = res;
148 if (resource.value > res.value) {
149 resource.value -= res.value;
150 } else {
151 onLastRemoved(res, info.uid);
152 actualRemoved.value = resource.value;
153 info.resources.erase(resType);
154 }
155
156 // Add it to the list of removed resources for observers.
157 auto it = resourceRemoved.find(resType);
158 if (it == resourceRemoved.end()) {
159 resourceRemoved[resType] = actualRemoved;
160 } else {
161 mergeResources(it->second, actualRemoved);
162 }
163 }
164 }
165 if (mObserverService != nullptr && !resourceRemoved.empty()) {
166 mObserverService->onResourceRemoved(info.uid, pid, resourceRemoved);
167 }
168 return true;
169}
170
171bool ResourceTracker::removeResource(const ClientInfoParcel& clientInfo, bool validateCallingPid) {
172 int32_t pid = clientInfo.pid;
173 int64_t clientId = clientInfo.id;
174
175 if (validateCallingPid && !mProcessInfo->isPidTrusted(pid)) {
176 pid_t callingPid = IPCThreadState::self()->getCallingPid();
177 ALOGW("%s called with untrusted pid %d, using calling pid %d", __func__,
178 pid, callingPid);
179 pid = callingPid;
180 }
181 std::map<int, ResourceInfos>::iterator found = mMap.find(pid);
182 if (found == mMap.end()) {
183 ALOGV("%s: didn't find pid %d for clientId %lld", __func__, pid, (long long) clientId);
184 return false;
185 }
186
187 ResourceInfos& infos = found->second;
188 ResourceInfos::iterator foundClient = infos.find(clientId);
189 if (foundClient == infos.end()) {
190 ALOGV("%s: didn't find clientId %lld", __func__, (long long) clientId);
191 return false;
192 }
193
194 const ResourceInfo& info = foundClient->second;
195 for (auto& [resType, resParcel] : info.resources) {
196 onLastRemoved(resParcel, info.uid);
197 }
198
199 if (mObserverService != nullptr && !info.resources.empty()) {
200 mObserverService->onResourceRemoved(info.uid, pid, info.resources);
201 }
202
203 infos.erase(foundClient);
204 return true;
205}
206
207std::shared_ptr<IResourceManagerClient> ResourceTracker::getClient(
208 int pid, const int64_t& clientId) const {
209 std::map<int, ResourceInfos>::const_iterator found = mMap.find(pid);
210 if (found == mMap.end()) {
211 ALOGV("%s: didn't find pid %d for clientId %lld", __func__, pid, (long long) clientId);
212 return nullptr;
213 }
214
215 const ResourceInfos& infos = found->second;
216 ResourceInfos::const_iterator foundClient = infos.find(clientId);
217 if (foundClient == infos.end()) {
218 ALOGV("%s: didn't find clientId %lld", __func__, (long long) clientId);
219 return nullptr;
220 }
221
222 return foundClient->second.client;
223}
224
225bool ResourceTracker::removeClient(int pid, const int64_t& clientId) {
226 std::map<int, ResourceInfos>::iterator found = mMap.find(pid);
227 if (found == mMap.end()) {
228 ALOGV("%s: didn't find pid %d for clientId %lld", __func__, pid, (long long) clientId);
229 return false;
230 }
231
232 ResourceInfos& infos = found->second;
233 ResourceInfos::iterator foundClient = infos.find(clientId);
234 if (foundClient == infos.end()) {
235 ALOGV("%s: didn't find clientId %lld", __func__, (long long) clientId);
236 return false;
237 }
238
239 infos.erase(foundClient);
240 return true;
241}
242
243bool ResourceTracker::markClientForPendingRemoval(const ClientInfoParcel& clientInfo) {
244 int32_t pid = clientInfo.pid;
245 int64_t clientId = clientInfo.id;
246
247 if (!mProcessInfo->isPidTrusted(pid)) {
248 pid_t callingPid = IPCThreadState::self()->getCallingPid();
249 ALOGW("%s called with untrusted pid %d, using calling pid %d", __func__,
250 pid, callingPid);
251 pid = callingPid;
252 }
253 std::map<int, ResourceInfos>::iterator found = mMap.find(pid);
254 if (found == mMap.end()) {
255 ALOGV("%s: didn't find pid %d for clientId %lld", __func__, pid, (long long)clientId);
256 return false;
257 }
258
259 ResourceInfos& infos = found->second;
260 ResourceInfos::iterator foundClient = infos.find(clientId);
261 if (foundClient == infos.end()) {
262 ALOGV("%s: didn't find clientId %lld", __func__, (long long) clientId);
263 return false;
264 }
265
266 ResourceInfo& info = foundClient->second;
267 info.pendingRemoval = true;
268 return true;
269}
270
271bool ResourceTracker::getClientsMarkedPendingRemoval(int32_t pid,
272 std::vector<ClientInfo>& targetClients) {
273 if (!mProcessInfo->isPidTrusted(pid)) {
274 pid_t callingPid = IPCThreadState::self()->getCallingPid();
275 ALOGW("%s called with untrusted pid %d, using calling pid %d", __func__, pid, callingPid);
276 pid = callingPid;
277 }
278
279 // Go through all the MediaResource types (and corresponding subtypes for
280 // each, if applicable) and see if the process (with given pid) holds any
281 // such resources that are marked as pending removal.
282 // Since the use-case of this function is to get all such resources (pending
283 // removal) and reclaim them all - the order in which we look for the
284 // resource type doesn't matter.
285 for (MediaResource::Type type : {MediaResource::Type::kSecureCodec,
286 MediaResource::Type::kNonSecureCodec,
287 MediaResource::Type::kGraphicMemory,
288 MediaResource::Type::kDrmSession}) {
289 switch (type) {
290 // Codec resources are segregated by audio, video and image domains.
291 case MediaResource::Type::kSecureCodec:
292 case MediaResource::Type::kNonSecureCodec:
293 for (MediaResource::SubType subType : {MediaResource::SubType::kHwAudioCodec,
294 MediaResource::SubType::kSwAudioCodec,
295 MediaResource::SubType::kHwVideoCodec,
296 MediaResource::SubType::kSwVideoCodec,
297 MediaResource::SubType::kHwImageCodec,
298 MediaResource::SubType::kSwImageCodec}) {
299 ClientInfo clientInfo;
300 if (getBiggestClient(pid, type, subType, clientInfo, true)) {
301 targetClients.emplace_back(clientInfo);
302 continue;
303 }
304 }
305 break;
306 // Non-codec resources are shared by audio, video and image codecs (no subtype).
307 default:
308 ClientInfo clientInfo;
309 MediaResource::SubType subType = MediaResource::SubType::kUnspecifiedSubType;
310 if (getBiggestClient(pid, type, subType, clientInfo, true)) {
311 targetClients.emplace_back(clientInfo);
312 }
313 break;
314 }
315 }
316
317 return true;
318}
319
320bool ResourceTracker::overridePid(int originalPid, int newPid) {
321 mOverridePidMap.erase(originalPid);
322 if (newPid != -1) {
323 mOverridePidMap.emplace(originalPid, newPid);
324 return true;
325 }
326 return false;
327}
328
329bool ResourceTracker::overrideProcessInfo(const std::shared_ptr<IResourceManagerClient>& client,
330 int pid, int procState, int oomScore) {
331 removeProcessInfoOverride(pid);
332
333 if (!mProcessInfo->overrideProcessInfo(pid, procState, oomScore)) {
334 // Override value is rejected by ProcessInfo.
335 return false;
336 }
337
338 ClientInfoParcel clientInfo{.pid = static_cast<int32_t>(pid),
339 .uid = 0,
340 .id = 0,
341 .name = "<unknown client>"};
342 std::shared_ptr<DeathNotifier> deathNotifier =
343 DeathNotifier::Create(client, mService, clientInfo, true);
344
345 mProcessInfoOverrideMap.emplace(pid, ProcessInfoOverride{deathNotifier, client});
346
347 return true;
348}
349
350void ResourceTracker::removeProcessInfoOverride(int pid) {
351 auto it = mProcessInfoOverrideMap.find(pid);
352 if (it == mProcessInfoOverrideMap.end()) {
353 return;
354 }
355
356 mProcessInfo->removeProcessInfoOverride(pid);
357 mProcessInfoOverrideMap.erase(pid);
358}
359
360bool ResourceTracker::getAllClients(const ResourceRequestInfo& resourceRequestInfo,
361 std::vector<ClientInfo>& clients) {
362 MediaResource::Type type = resourceRequestInfo.mResource->type;
363 MediaResource::SubType subType = resourceRequestInfo.mResource->subType;
364 bool foundClient = false;
365
366 for (auto& [pid, /* ResourceInfos */ infos] : mMap) {
367 for (auto& [id, /* ResourceInfo */ info] : infos) {
368 if (hasResourceType(type, subType, info.resources)) {
369 clients.emplace_back(info.pid, info.uid, info.clientId);
370 foundClient = true;
371 }
372 }
373 }
374
375 return foundClient;
376}
377
378bool ResourceTracker::getLowestPriorityPid(MediaResource::Type type, MediaResource::SubType subType,
379 int& lowestPriorityPid, int& lowestPriority) {
380 int pid = -1;
381 int priority = -1;
382 for (auto& [tempPid, /* ResourceInfos */ infos] : mMap) {
383 if (infos.size() == 0) {
384 // no client on this process.
385 continue;
386 }
387 if (!hasResourceType(type, subType, infos)) {
388 // doesn't have the requested resource type
389 continue;
390 }
391 int tempPriority = -1;
392 if (!getPriority(tempPid, &tempPriority)) {
393 ALOGV("%s: can't get priority of pid %d, skipped", __func__, tempPid);
394 // TODO: remove this pid from mMap?
395 continue;
396 }
397 if (pid == -1 || tempPriority > priority) {
398 // initial the value
399 pid = tempPid;
400 priority = tempPriority;
401 }
402 }
403
404 bool success = (pid != -1);
405
406 if (success) {
407 lowestPriorityPid = pid;
408 lowestPriority = priority;
409 }
410 return success;
411}
412
413bool ResourceTracker::getBiggestClient(int pid, MediaResource::Type type,
414 MediaResource::SubType subType,
415 ClientInfo& clientInfo, bool pendingRemovalOnly) {
416 std::map<int, ResourceInfos>::iterator found = mMap.find(pid);
417 if (found == mMap.end()) {
418 ALOGE_IF(!pendingRemovalOnly, "%s: can't find resource info for pid %d", __func__, pid);
419 return false;
420 }
421
422 uid_t uid = -1;
423 int64_t clientId = -1;
424 uint64_t largestValue = 0;
425 const ResourceInfos& infos = found->second;
426 for (const auto& [id, /* ResourceInfo */ info] : infos) {
427 const ResourceList& resources = info.resources;
428 if (pendingRemovalOnly && !info.pendingRemoval) {
429 continue;
430 }
431 for (auto it = resources.begin(); it != resources.end(); it++) {
432 const MediaResourceParcel &resource = it->second;
433 if (hasResourceType(type, subType, resource)) {
434 if (resource.value > largestValue) {
435 largestValue = resource.value;
436 clientId = info.clientId;
437 uid = info.uid;
438 }
439 }
440 }
441 }
442
443 if (clientId == -1) {
444 ALOGE_IF(!pendingRemovalOnly,
445 "%s: can't find resource type %s and subtype %s for pid %d",
446 __func__, asString(type), asString(subType), pid);
447 return false;
448 }
449
450 clientInfo.mPid = pid;
451 clientInfo.mUid = uid;
452 clientInfo.mClientId = clientId;
453 return true;
454}
455
456void ResourceTracker::dump(std::string& resourceLogs) {
457 const size_t SIZE = 256;
458 char buffer[SIZE];
459 resourceLogs.append(" Processes:\n");
460 for (const auto& [pid, /* ResourceInfos */ infos] : mMap) {
461 snprintf(buffer, SIZE, " Pid: %d\n", pid);
462 resourceLogs.append(buffer);
463 int priority = 0;
464 if (getPriority(pid, &priority)) {
465 snprintf(buffer, SIZE, " Priority: %d\n", priority);
466 } else {
467 snprintf(buffer, SIZE, " Priority: <unknown>\n");
468 }
469 resourceLogs.append(buffer);
470
471 for (const auto& [infoKey, /* ResourceInfo */ info] : infos) {
472 resourceLogs.append(" Client:\n");
473 snprintf(buffer, SIZE, " Id: %lld\n", (long long)info.clientId);
474 resourceLogs.append(buffer);
475
476 std::string clientName = info.name;
477 snprintf(buffer, SIZE, " Name: %s\n", clientName.c_str());
478 resourceLogs.append(buffer);
479
480 const ResourceList& resources = info.resources;
481 resourceLogs.append(" Resources:\n");
482 for (auto it = resources.begin(); it != resources.end(); it++) {
483 snprintf(buffer, SIZE, " %s\n", toString(it->second).c_str());
484 resourceLogs.append(buffer);
485 }
486 }
487 }
488 resourceLogs.append(" Process Pid override:\n");
489 for (const auto& [oldPid, newPid] : mOverridePidMap) {
490 snprintf(buffer, SIZE, " Original Pid: %d, Override Pid: %d\n", oldPid, newPid);
491 resourceLogs.append(buffer);
492 }
493}
494
495void ResourceTracker::onFirstAdded(const MediaResourceParcel& resource, uid_t uid) {
496 std::shared_ptr<ResourceManagerServiceNew> service = mService.lock();
497 if (service == nullptr) {
498 ALOGW("%s: ResourceManagerService is invalid!", __func__);
499 return;
500 }
501
502 service->onFirstAdded(resource, uid);
503}
504
505void ResourceTracker::onLastRemoved(const MediaResourceParcel& resource, uid_t uid) {
506 std::shared_ptr<ResourceManagerServiceNew> service = mService.lock();
507 if (service == nullptr) {
508 ALOGW("%s: ResourceManagerService is invalid!", __func__);
509 return;
510 }
511
512 service->onLastRemoved(resource, uid);
513}
514
515bool ResourceTracker::getPriority(int pid, int* priority) {
516 int newPid = pid;
517
518 if (mOverridePidMap.find(pid) != mOverridePidMap.end()) {
519 newPid = mOverridePidMap[pid];
520 ALOGD("%s: use override pid %d instead original pid %d", __func__, newPid, pid);
521 }
522
523 return mProcessInfo->getPriority(newPid, priority);
524}
525
526bool ResourceTracker::getNonConflictingClients(const ResourceRequestInfo& resourceRequestInfo,
527 std::vector<ClientInfo>& clients) {
528 MediaResource::Type type = resourceRequestInfo.mResource->type;
529 MediaResource::SubType subType = resourceRequestInfo.mResource->subType;
530 for (auto& [pid, /* ResourceInfos */ infos] : mMap) {
531 for (const auto& [id, /* ResourceInfo */ info] : infos) {
532 if (hasResourceType(type, subType, info.resources)) {
533 if (!isCallingPriorityHigher(resourceRequestInfo.mCallingPid, pid)) {
534 // some higher/equal priority process owns the resource,
535 // this is a conflict.
536 ALOGE("%s: The resource (%s) request from pid %d is conflicting",
537 __func__, asString(type), pid);
538 clients.clear();
539 return false;
540 } else {
541 clients.emplace_back(info.pid, info.uid, info.clientId);
542 }
543 }
544 }
545 }
546
547 return true;
548}
549
550bool ResourceTracker::isCallingPriorityHigher(int callingPid, int pid) {
551 int callingPidPriority;
552 if (!getPriority(callingPid, &callingPidPriority)) {
553 return false;
554 }
555
556 int priority;
557 if (!getPriority(pid, &priority)) {
558 return false;
559 }
560
561 return (callingPidPriority < priority);
562}
563
564} // namespace android