blob: 679ab13af9dcb48d64522a719bb1b55a4f95e0b9 [file] [log] [blame]
Girish27365ed2023-10-11 20:20:55 +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 "ResourceManagerServiceUtils"
20#include <utils/Log.h>
21
Girish6a6044d2023-11-22 21:23:14 +000022#include <binder/IServiceManager.h>
23
24#include "IMediaResourceMonitor.h"
Girish27365ed2023-10-11 20:20:55 +000025#include "ResourceManagerService.h"
26#include "ResourceManagerServiceUtils.h"
27
28namespace android {
29
Girishd11a03a2023-11-30 21:17:51 +000030bool ResourceList::add(const MediaResourceParcel& res, bool* isNewEntry) {
31 // See if it's an existing entry, if so, merge it.
32 for (MediaResourceParcel& item : mResourceList) {
33 if (item.type == res.type && item.subType == res.subType && item.id == res.id) {
34 // We already have an item. Merge them and return
35 mergeResources(item, res);
36 return true;
37 }
38 }
39
40 // Since we have't found this resource yet, it is a new entry.
41 // We can't init a new entry with negative value, although it's allowed
42 // to merge in negative values after the initial add.
43 if (res.value <= 0) {
44 ALOGW("Ignoring request to add new resource entry with value <= 0");
45 return false;
46 }
47 if (isNewEntry) {
48 *isNewEntry = true;
49 }
50 mResourceList.push_back(res);
51 return true;
52}
53
54void ResourceList::addOrUpdate(const MediaResourceParcel& res) {
55 // See if it's an existing entry, just update the value.
56 for (MediaResourceParcel& item : mResourceList) {
57 if (item.type == res.type && item.subType == res.subType && item.id == res.id) {
58 item.value = res.value;
59 return;
60 }
61 }
62
63 // Add the new entry.
64 mResourceList.push_back(res);
65}
66
67bool ResourceList::remove(const MediaResourceParcel& res, long* removedEntryValue) {
68 // Make sure we have an entry for this resource.
69 for (std::vector<MediaResourceParcel>::iterator it = mResourceList.begin();
70 it != mResourceList.end(); it++) {
71 if (it->type == res.type && it->subType == res.subType && it->id == res.id) {
72 if (it->value > res.value) {
73 // Subtract the resource value by given value.
74 it->value -= res.value;
75 } else {
76 // This entry will be removed.
77 if (removedEntryValue) {
78 *removedEntryValue = it->value;
79 }
80 mResourceList.erase(it);
81 }
82 return true;
83 }
84 }
85
86 // No such entry.
87 return false;
88}
89
90std::string ResourceList::toString() const {
91 std::string str;
92 for (const ::aidl::android::media::MediaResourceParcel& res : mResourceList) {
93 str.append(android::toString(res).c_str());
94 str.append("\n");
95 }
96
97 return std::move(str);
98}
99
100bool ResourceList::operator==(const ResourceList& rhs) const {
101 // Make sure the size is the same.
102 if (mResourceList.size() != rhs.mResourceList.size()) {
103 return false;
104 }
105
106 // Create a set from this object and check for the items from the rhs.
107 std::set<::aidl::android::media::MediaResourceParcel> lhs(
108 mResourceList.begin(), mResourceList.end());
109 for (const ::aidl::android::media::MediaResourceParcel& res : rhs.mResourceList) {
110 if (lhs.find(res) == lhs.end()) {
111 return false;
112 }
113 }
114 return true;
115}
116
Girishab17b0f2023-11-20 06:00:44 +0000117// Bunch of utility functions that looks for a specific Resource.
118// Check whether a given resource (of type and subtype) is found in given resource parcel.
Girish27365ed2023-10-11 20:20:55 +0000119bool hasResourceType(MediaResource::Type type, MediaResource::SubType subType,
Girishab17b0f2023-11-20 06:00:44 +0000120 const MediaResourceParcel& resource) {
Girish27365ed2023-10-11 20:20:55 +0000121 if (type != resource.type) {
122 return false;
123 }
124 switch (type) {
Girishab17b0f2023-11-20 06:00:44 +0000125 // Codec subtypes (e.g. video vs. audio and hw vs. sw) are each considered separate resources,
126 // so compare the subtypes as well.
127 case MediaResource::Type::kSecureCodec:
128 case MediaResource::Type::kNonSecureCodec:
129 if (resource.subType == subType) {
Girish27365ed2023-10-11 20:20:55 +0000130 return true;
Girishab17b0f2023-11-20 06:00:44 +0000131 }
132 break;
133 // Non-codec resources are not segregated by the subtype (e.g. video vs. audio).
134 default:
135 return true;
Girish27365ed2023-10-11 20:20:55 +0000136 }
137 return false;
138}
139
Girishab17b0f2023-11-20 06:00:44 +0000140// Check whether a given resource (of type and subtype) is found in given resource list.
Girish27365ed2023-10-11 20:20:55 +0000141bool hasResourceType(MediaResource::Type type, MediaResource::SubType subType,
Girishab17b0f2023-11-20 06:00:44 +0000142 const ResourceList& resources) {
Girishd11a03a2023-11-30 21:17:51 +0000143 for (const MediaResourceParcel& res : resources.getResources()) {
144 if (hasResourceType(type, subType, res)) {
Girish27365ed2023-10-11 20:20:55 +0000145 return true;
146 }
147 }
148 return false;
149}
150
Girishab17b0f2023-11-20 06:00:44 +0000151// Check whether a given resource (of type and subtype) is found in given resource info list.
Girish27365ed2023-10-11 20:20:55 +0000152bool hasResourceType(MediaResource::Type type, MediaResource::SubType subType,
Girishab17b0f2023-11-20 06:00:44 +0000153 const ResourceInfos& infos) {
Girish27365ed2023-10-11 20:20:55 +0000154 for (const auto& [id, info] : infos) {
155 if (hasResourceType(type, subType, info.resources)) {
156 return true;
157 }
158 }
159 return false;
160}
161
162ResourceInfos& getResourceInfosForEdit(int pid, PidResourceInfosMap& map) {
163 PidResourceInfosMap::iterator found = map.find(pid);
164 if (found == map.end()) {
165 // new pid
166 ResourceInfos infosForPid;
167 auto [it, inserted] = map.emplace(pid, infosForPid);
168 found = it;
169 }
170
171 return found->second;
172}
173
Girishab17b0f2023-11-20 06:00:44 +0000174// Return modifiable ResourceInfo for a given client (look up by client id)
175// from the map of ResourceInfos.
176// If the item is not in the map, create one and add it to the map.
Girish27365ed2023-10-11 20:20:55 +0000177ResourceInfo& getResourceInfoForEdit(const ClientInfoParcel& clientInfo,
Girishab17b0f2023-11-20 06:00:44 +0000178 const std::shared_ptr<IResourceManagerClient>& client,
179 ResourceInfos& infos) {
Girish27365ed2023-10-11 20:20:55 +0000180 ResourceInfos::iterator found = infos.find(clientInfo.id);
Girish27365ed2023-10-11 20:20:55 +0000181 if (found == infos.end()) {
Girishab17b0f2023-11-20 06:00:44 +0000182 ResourceInfo info{.pid = clientInfo.pid,
183 .uid = static_cast<uid_t>(clientInfo.uid),
Girish27365ed2023-10-11 20:20:55 +0000184 .clientId = clientInfo.id,
185 .name = clientInfo.name.empty()? "<unknown client>" : clientInfo.name,
186 .client = client,
187 .deathNotifier = nullptr,
Girish88a83502023-11-23 11:23:07 +0000188 .pendingRemoval = false,
189 .importance = static_cast<uint32_t>(std::max(0, clientInfo.importance))};
Girish27365ed2023-10-11 20:20:55 +0000190 auto [it, inserted] = infos.emplace(clientInfo.id, info);
191 found = it;
192 }
193
194 return found->second;
195}
196
Girishab17b0f2023-11-20 06:00:44 +0000197// Merge resources from r2 into r1.
198void mergeResources(MediaResourceParcel& r1, const MediaResourceParcel& r2) {
199 // The resource entry on record is maintained to be in [0,INT64_MAX].
200 // Clamp if merging in the new resource value causes it to go out of bound.
201 // Note that the new resource value could be negative, eg.DrmSession, the
202 // value goes lower when the session is used more often. During reclaim
203 // the session with the highest value (lowest usage) would be closed.
204 if (r2.value < INT64_MAX - r1.value) {
205 r1.value += r2.value;
206 if (r1.value < 0) {
207 r1.value = 0;
208 }
209 } else {
210 r1.value = INT64_MAX;
211 }
212}
213
214///////////////////////////////////////////////////////////////////////
215////////////// Death Notifier implementation ////////////////////////
216///////////////////////////////////////////////////////////////////////
217
218DeathNotifier::DeathNotifier(const std::shared_ptr<IResourceManagerClient>& client,
219 const std::weak_ptr<ResourceManagerService>& service,
220 const ClientInfoParcel& clientInfo)
221 : mClient(client), mService(service), mClientInfo(clientInfo),
222 mCookie(nullptr),
223 mDeathRecipient(::ndk::ScopedAIBinder_DeathRecipient(
224 AIBinder_DeathRecipient_new(BinderDiedCallback))) {
225 // Setting callback notification when DeathRecipient gets deleted.
226 AIBinder_DeathRecipient_setOnUnlinked(mDeathRecipient.get(), BinderUnlinkedCallback);
227}
228
229//static
230void DeathNotifier::BinderUnlinkedCallback(void* cookie) {
231 BinderDiedContext* context = reinterpret_cast<BinderDiedContext*>(cookie);
232 // Since we don't need the context anymore, we are deleting it now.
233 delete context;
234}
235
236//static
237void DeathNotifier::BinderDiedCallback(void* cookie) {
238 BinderDiedContext* context = reinterpret_cast<BinderDiedContext*>(cookie);
239
240 // Validate the context and check if the DeathNotifier object is still in scope.
241 if (context != nullptr) {
242 std::shared_ptr<DeathNotifier> thiz = context->mDeathNotifier.lock();
243 if (thiz != nullptr) {
244 thiz->binderDied();
245 } else {
246 ALOGI("DeathNotifier is out of scope already");
247 }
248 }
249}
250
251void DeathNotifier::binderDied() {
252 // Don't check for pid validity since we know it's already dead.
253 std::shared_ptr<ResourceManagerService> service = mService.lock();
254 if (service == nullptr) {
255 ALOGW("ResourceManagerService is dead as well.");
256 return;
257 }
258
259 service->overridePid(mClientInfo.pid, -1);
260 // thiz is freed in the call below, so it must be last call referring thiz
261 service->removeResource(mClientInfo, false /*checkValid*/);
262}
263
264void OverrideProcessInfoDeathNotifier::binderDied() {
265 // Don't check for pid validity since we know it's already dead.
266 std::shared_ptr<ResourceManagerService> service = mService.lock();
267 if (service == nullptr) {
268 ALOGW("ResourceManagerService is dead as well.");
269 return;
270 }
271
272 service->removeProcessInfoOverride(mClientInfo.pid);
273}
274
275std::shared_ptr<DeathNotifier> DeathNotifier::Create(
276 const std::shared_ptr<IResourceManagerClient>& client,
277 const std::weak_ptr<ResourceManagerService>& service,
278 const ClientInfoParcel& clientInfo,
279 bool overrideProcessInfo) {
280 std::shared_ptr<DeathNotifier> deathNotifier = nullptr;
281 if (overrideProcessInfo) {
282 deathNotifier = std::make_shared<OverrideProcessInfoDeathNotifier>(
283 client, service, clientInfo);
284 } else {
285 deathNotifier = std::make_shared<DeathNotifier>(client, service, clientInfo);
286 }
287
288 if (deathNotifier) {
289 deathNotifier->link();
290 }
291
292 return deathNotifier;
293}
294
Girish6a6044d2023-11-22 21:23:14 +0000295void notifyResourceGranted(int pid, const std::vector<MediaResourceParcel>& resources) {
296 static const char* const kServiceName = "media_resource_monitor";
297 sp<IBinder> binder = defaultServiceManager()->checkService(String16(kServiceName));
298 if (binder != NULL) {
299 sp<IMediaResourceMonitor> service = interface_cast<IMediaResourceMonitor>(binder);
300 for (size_t i = 0; i < resources.size(); ++i) {
301 switch (resources[i].subType) {
302 case MediaResource::SubType::kHwAudioCodec:
303 case MediaResource::SubType::kSwAudioCodec:
304 service->notifyResourceGranted(pid, IMediaResourceMonitor::TYPE_AUDIO_CODEC);
305 break;
306 case MediaResource::SubType::kHwVideoCodec:
307 case MediaResource::SubType::kSwVideoCodec:
308 service->notifyResourceGranted(pid, IMediaResourceMonitor::TYPE_VIDEO_CODEC);
309 break;
310 case MediaResource::SubType::kHwImageCodec:
311 case MediaResource::SubType::kSwImageCodec:
312 service->notifyResourceGranted(pid, IMediaResourceMonitor::TYPE_IMAGE_CODEC);
313 break;
314 case MediaResource::SubType::kUnspecifiedSubType:
315 break;
316 }
317 }
318 }
319}
320
Girish27365ed2023-10-11 20:20:55 +0000321} // namespace android