• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
22 #include <binder/IServiceManager.h>
23 
24 #include "IMediaResourceMonitor.h"
25 #include "ResourceManagerService.h"
26 #include "ResourceManagerServiceUtils.h"
27 
28 namespace android {
29 
add(const MediaResourceParcel & res,bool * isNewEntry)30 bool 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 
addOrUpdate(const MediaResourceParcel & res)54 void 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 
remove(const MediaResourceParcel & res,long * removedEntryValue)67 bool 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 
update(const MediaResourceParcel & res,long * removedEntryValue)90 bool ResourceList::update(const MediaResourceParcel& res, long* removedEntryValue) {
91     for (std::vector<MediaResourceParcel>::iterator it = mResourceList.begin();
92          it != mResourceList.end(); it++) {
93         if (it->type == res.type && it->subType == res.subType && it->id == res.id) {
94             if (res.value == 0) {
95                 // This entry will be removed.
96                 if (removedEntryValue) {
97                     *removedEntryValue = it->value;
98                 }
99                 mResourceList.erase(it);
100             } else {
101                 // Update the new value.
102                 it->value = res.value;
103             }
104             return true;
105         }
106     }
107 
108     // Add the new entry.
109     mResourceList.push_back(res);
110     return false;
111 }
112 
toString() const113 std::string ResourceList::toString() const {
114     std::string str;
115     for (const ::aidl::android::media::MediaResourceParcel& res : mResourceList) {
116         str.append(android::toString(res).c_str());
117         str.append("\n");
118     }
119 
120     return str;
121 }
122 
operator ==(const ResourceList & rhs) const123 bool ResourceList::operator==(const ResourceList& rhs) const {
124     // Make sure the size is the same.
125     if (mResourceList.size() != rhs.mResourceList.size()) {
126         return false;
127     }
128 
129     // Create a set from this object and check for the items from the rhs.
130     std::set<::aidl::android::media::MediaResourceParcel> lhs(
131             mResourceList.begin(), mResourceList.end());
132     for (const ::aidl::android::media::MediaResourceParcel& res : rhs.mResourceList) {
133         if (lhs.find(res) == lhs.end()) {
134             return false;
135         }
136     }
137     return true;
138 }
139 
140 // Bunch of utility functions that looks for a specific Resource.
141 // Check whether a given resource (of type and subtype) is found in given resource parcel.
hasResourceType(MediaResource::Type type,MediaResource::SubType subType,const MediaResourceParcel & resource)142 bool hasResourceType(MediaResource::Type type, MediaResource::SubType subType,
143                      const MediaResourceParcel& resource) {
144     if (type != resource.type) {
145       return false;
146     }
147     switch (type) {
148     // Codec subtypes (e.g. video vs. audio and hw vs. sw) are each considered separate resources,
149     // so compare the subtypes as well.
150     case MediaResource::Type::kSecureCodec:
151     case MediaResource::Type::kNonSecureCodec:
152         if (resource.subType == subType) {
153             return true;
154         }
155         break;
156     // Non-codec resources are not segregated by the subtype (e.g. video vs. audio).
157     default:
158         return true;
159     }
160     return false;
161 }
162 
163 // Check whether a given resource (of type and subtype) is found in given resource list.
hasResourceType(MediaResource::Type type,MediaResource::SubType subType,const ResourceList & resources)164 bool hasResourceType(MediaResource::Type type, MediaResource::SubType subType,
165                      const ResourceList& resources) {
166     for (const MediaResourceParcel& res : resources.getResources()) {
167         if (hasResourceType(type, subType, res)) {
168             return true;
169         }
170     }
171     return false;
172 }
173 
174 // Check whether a given resource (of type and subtype) is found in given resource info list.
hasResourceType(MediaResource::Type type,MediaResource::SubType subType,const ResourceInfos & infos)175 bool hasResourceType(MediaResource::Type type, MediaResource::SubType subType,
176                      const ResourceInfos& infos) {
177     for (const auto& [id, info] : infos) {
178         if (hasResourceType(type, subType, info.resources)) {
179             return true;
180         }
181     }
182     return false;
183 }
184 
getResourceInfosForEdit(int pid,PidResourceInfosMap & map)185 ResourceInfos& getResourceInfosForEdit(int pid, PidResourceInfosMap& map) {
186     PidResourceInfosMap::iterator found = map.find(pid);
187     if (found == map.end()) {
188         // new pid
189         ResourceInfos infosForPid;
190         auto [it, inserted] = map.emplace(pid, infosForPid);
191         found = it;
192     }
193 
194     return found->second;
195 }
196 
197 // Return modifiable ResourceInfo for a given client (look up by client id)
198 // from the map of ResourceInfos.
199 // If the item is not in the map, create one and add it to the map.
getResourceInfoForEdit(const ClientInfoParcel & clientInfo,const std::shared_ptr<IResourceManagerClient> & client,ResourceInfos & infos)200 ResourceInfo& getResourceInfoForEdit(const ClientInfoParcel& clientInfo,
201                                      const std::shared_ptr<IResourceManagerClient>& client,
202                                      ResourceInfos& infos) {
203     ResourceInfos::iterator found = infos.find(clientInfo.id);
204     if (found == infos.end()) {
205         ResourceInfo info{.pid = clientInfo.pid,
206                           .uid = static_cast<uid_t>(clientInfo.uid),
207                           .clientId = clientInfo.id,
208                           .name = clientInfo.name.empty()? "<unknown client>" : clientInfo.name,
209                           .client = client,
210                           .deathNotifier = nullptr,
211                           .pendingRemoval = false,
212                           .importance = static_cast<uint32_t>(std::max(0, clientInfo.importance))};
213         auto [it, inserted] = infos.emplace(clientInfo.id, info);
214         found = it;
215     }
216 
217     return found->second;
218 }
219 
220 // Merge resources from r2 into r1.
mergeResources(MediaResourceParcel & r1,const MediaResourceParcel & r2)221 void mergeResources(MediaResourceParcel& r1, const MediaResourceParcel& r2) {
222     // The resource entry on record is maintained to be in [0,INT64_MAX].
223     // Clamp if merging in the new resource value causes it to go out of bound.
224     // Note that the new resource value could be negative, eg.DrmSession, the
225     // value goes lower when the session is used more often. During reclaim
226     // the session with the highest value (lowest usage) would be closed.
227     if (r2.value < INT64_MAX - r1.value) {
228         r1.value += r2.value;
229         if (r1.value < 0) {
230             r1.value = 0;
231         }
232     } else {
233         r1.value = INT64_MAX;
234     }
235 }
236 
237 ///////////////////////////////////////////////////////////////////////
238 ////////////// Death Notifier implementation   ////////////////////////
239 ///////////////////////////////////////////////////////////////////////
240 
DeathNotifier(const std::shared_ptr<IResourceManagerClient> & client,const std::weak_ptr<ResourceManagerService> & service,const ClientInfoParcel & clientInfo)241 DeathNotifier::DeathNotifier(const std::shared_ptr<IResourceManagerClient>& client,
242                              const std::weak_ptr<ResourceManagerService>& service,
243                              const ClientInfoParcel& clientInfo)
244     : mClient(client), mService(service), mClientInfo(clientInfo),
245       mCookie(nullptr),
246       mDeathRecipient(::ndk::ScopedAIBinder_DeathRecipient(
247                       AIBinder_DeathRecipient_new(BinderDiedCallback))) {
248     // Setting callback notification when DeathRecipient gets deleted.
249     AIBinder_DeathRecipient_setOnUnlinked(mDeathRecipient.get(), BinderUnlinkedCallback);
250 }
251 
252 //static
BinderUnlinkedCallback(void * cookie)253 void DeathNotifier::BinderUnlinkedCallback(void* cookie) {
254     BinderDiedContext* context = reinterpret_cast<BinderDiedContext*>(cookie);
255     // Since we don't need the context anymore, we are deleting it now.
256     delete context;
257 }
258 
259 //static
BinderDiedCallback(void * cookie)260 void DeathNotifier::BinderDiedCallback(void* cookie) {
261     BinderDiedContext* context = reinterpret_cast<BinderDiedContext*>(cookie);
262 
263     // Validate the context and check if the DeathNotifier object is still in scope.
264     if (context != nullptr) {
265         std::shared_ptr<DeathNotifier> thiz = context->mDeathNotifier.lock();
266         if (thiz != nullptr) {
267             thiz->binderDied();
268         } else {
269             ALOGI("DeathNotifier is out of scope already");
270         }
271     }
272 }
273 
binderDied()274 void DeathNotifier::binderDied() {
275     // Don't check for pid validity since we know it's already dead.
276     std::shared_ptr<ResourceManagerService> service = mService.lock();
277     if (service == nullptr) {
278         ALOGW("ResourceManagerService is dead as well.");
279         return;
280     }
281 
282     service->overridePid(mClientInfo.pid, -1);
283     // thiz is freed in the call below, so it must be last call referring thiz
284     service->removeResource(mClientInfo, false /*checkValid*/);
285 }
286 
binderDied()287 void OverrideProcessInfoDeathNotifier::binderDied() {
288     // Don't check for pid validity since we know it's already dead.
289     std::shared_ptr<ResourceManagerService> service = mService.lock();
290     if (service == nullptr) {
291         ALOGW("ResourceManagerService is dead as well.");
292         return;
293     }
294 
295     service->removeProcessInfoOverride(mClientInfo.pid);
296 }
297 
Create(const std::shared_ptr<IResourceManagerClient> & client,const std::weak_ptr<ResourceManagerService> & service,const ClientInfoParcel & clientInfo,bool overrideProcessInfo)298 std::shared_ptr<DeathNotifier> DeathNotifier::Create(
299     const std::shared_ptr<IResourceManagerClient>& client,
300     const std::weak_ptr<ResourceManagerService>& service,
301     const ClientInfoParcel& clientInfo,
302     bool overrideProcessInfo) {
303     std::shared_ptr<DeathNotifier> deathNotifier = nullptr;
304     if (overrideProcessInfo) {
305         deathNotifier = std::make_shared<OverrideProcessInfoDeathNotifier>(
306             client, service, clientInfo);
307     } else {
308         deathNotifier = std::make_shared<DeathNotifier>(client, service, clientInfo);
309     }
310 
311     if (deathNotifier) {
312         deathNotifier->link();
313     }
314 
315     return deathNotifier;
316 }
317 
notifyResourceGranted(int pid,const std::vector<MediaResourceParcel> & resources)318 void notifyResourceGranted(int pid, const std::vector<MediaResourceParcel>& resources) {
319     static const char* const kServiceName = "media_resource_monitor";
320     sp<IBinder> binder = defaultServiceManager()->checkService(String16(kServiceName));
321     if (binder != NULL) {
322         sp<IMediaResourceMonitor> service = interface_cast<IMediaResourceMonitor>(binder);
323         for (size_t i = 0; i < resources.size(); ++i) {
324             switch (resources[i].subType) {
325                 case MediaResource::SubType::kHwAudioCodec:
326                 case MediaResource::SubType::kSwAudioCodec:
327                     service->notifyResourceGranted(pid, IMediaResourceMonitor::TYPE_AUDIO_CODEC);
328                     break;
329                 case MediaResource::SubType::kHwVideoCodec:
330                 case MediaResource::SubType::kSwVideoCodec:
331                     service->notifyResourceGranted(pid, IMediaResourceMonitor::TYPE_VIDEO_CODEC);
332                     break;
333                 case MediaResource::SubType::kHwImageCodec:
334                 case MediaResource::SubType::kSwImageCodec:
335                     service->notifyResourceGranted(pid, IMediaResourceMonitor::TYPE_IMAGE_CODEC);
336                     break;
337                 case MediaResource::SubType::kUnspecifiedSubType:
338                     break;
339             }
340         }
341     }
342 }
343 
344 } // namespace android
345