Add resource observer service
Add resource observer service so that clients can
register listeners to receive resource status change.
This is needed by transcoding service to properly
handle media resource lost. Resource lost itself is
detected by exceptions on MediaCodec, however in
order to resume the service, a notification from
ResourceManager is needed.
Note that the observer service is built as a separate
service so that client can access it without requesting
full resourcemanager service. It also allows us to
list the observer service interface as a stable interface
without having to lock down resourcemanager interface.
bug: 168307955
bug: 154733526
test:
- unit testing in this CL;
- manually tested using the service from media.transcoding
and got codec updates and "adb shell dumpsys"
Change-Id: I8028c605600a40c1aa2c89850ef877a5bf1d0c09
diff --git a/services/mediaresourcemanager/ResourceObserverService.cpp b/services/mediaresourcemanager/ResourceObserverService.cpp
new file mode 100644
index 0000000..7c4c875
--- /dev/null
+++ b/services/mediaresourcemanager/ResourceObserverService.cpp
@@ -0,0 +1,312 @@
+/**
+ *
+ * Copyright 2020, 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 "ResourceObserverService"
+#include <utils/Log.h>
+
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <binder/IServiceManager.h>
+#include <utils/String16.h>
+#include <aidl/android/media/MediaResourceParcel.h>
+
+#include "ResourceObserverService.h"
+
+namespace aidl {
+namespace android {
+namespace media {
+bool operator<(const MediaObservableFilter& lhs, const MediaObservableFilter &rhs) {
+ return lhs.type < rhs.type || (lhs.type == rhs.type && lhs.eventFilter < rhs.eventFilter);
+}
+}}} // namespace ::aidl::android::media
+
+namespace android {
+
+using ::aidl::android::media::MediaResourceParcel;
+using ::aidl::android::media::MediaObservableEvent;
+
+// MediaObservableEvent will be used as uint64_t flags.
+static_assert(sizeof(MediaObservableEvent) == sizeof(uint64_t));
+
+static std::vector<MediaObservableEvent> sEvents = {
+ MediaObservableEvent::kBusy,
+ MediaObservableEvent::kIdle,
+};
+
+static MediaObservableType getObservableType(const MediaResourceParcel& res) {
+ if (res.subType == MediaResourceSubType::kVideoCodec) {
+ if (res.type == MediaResourceType::kNonSecureCodec) {
+ return MediaObservableType::kVideoNonSecureCodec;
+ }
+ if (res.type == MediaResourceType::kSecureCodec) {
+ return MediaObservableType::kVideoSecureCodec;
+ }
+ }
+ return MediaObservableType::kInvalid;
+}
+
+//static
+std::mutex ResourceObserverService::sDeathRecipientLock;
+//static
+std::map<uintptr_t, std::shared_ptr<ResourceObserverService::DeathRecipient> >
+ResourceObserverService::sDeathRecipientMap;
+
+struct ResourceObserverService::DeathRecipient {
+ DeathRecipient(ResourceObserverService* _service,
+ const std::shared_ptr<IResourceObserver>& _observer)
+ : service(_service), observer(_observer) {}
+ ~DeathRecipient() {}
+
+ void binderDied() {
+ if (service != nullptr) {
+ service->unregisterObserver(observer);
+ }
+ }
+
+ ResourceObserverService* service;
+ std::shared_ptr<IResourceObserver> observer;
+};
+
+// static
+void ResourceObserverService::BinderDiedCallback(void* cookie) {
+ uintptr_t id = reinterpret_cast<uintptr_t>(cookie);
+
+ ALOGW("Observer %lld is dead", (long long)id);
+
+ std::shared_ptr<DeathRecipient> recipient;
+
+ {
+ std::scoped_lock lock{sDeathRecipientLock};
+
+ auto it = sDeathRecipientMap.find(id);
+ if (it != sDeathRecipientMap.end()) {
+ recipient = it->second;
+ }
+ }
+
+ if (recipient != nullptr) {
+ recipient->binderDied();
+ }
+}
+
+//static
+std::shared_ptr<ResourceObserverService> ResourceObserverService::instantiate() {
+ std::shared_ptr<ResourceObserverService> observerService =
+ ::ndk::SharedRefBase::make<ResourceObserverService>();
+ binder_status_t status = AServiceManager_addService(observerService->asBinder().get(),
+ ResourceObserverService::getServiceName());
+ if (status != STATUS_OK) {
+ return nullptr;
+ }
+ return observerService;
+}
+
+ResourceObserverService::ResourceObserverService()
+ : mDeathRecipient(AIBinder_DeathRecipient_new(BinderDiedCallback)) {}
+
+binder_status_t ResourceObserverService::dump(
+ int fd, const char** /*args*/, uint32_t /*numArgs*/) {
+ String8 result;
+
+ if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
+ result.format("Permission Denial: "
+ "can't dump ResourceManagerService from pid=%d, uid=%d\n",
+ AIBinder_getCallingPid(),
+ AIBinder_getCallingUid());
+ write(fd, result.string(), result.size());
+ return PERMISSION_DENIED;
+ }
+
+ result.appendFormat("ResourceObserverService: %p\n", this);
+ result.appendFormat(" Registered Observers: %zu\n", mObserverInfoMap.size());
+
+ {
+ std::scoped_lock lock{mObserverLock};
+
+ for (auto &observer : mObserverInfoMap) {
+ result.appendFormat(" Observer %p:\n", observer.second.binder.get());
+ for (auto &observable : observer.second.filters) {
+ String8 enabledEventsStr;
+ for (auto &event : sEvents) {
+ if (((uint64_t)observable.eventFilter & (uint64_t)event) != 0) {
+ if (!enabledEventsStr.isEmpty()) {
+ enabledEventsStr.append("|");
+ }
+ enabledEventsStr.append(toString(event).c_str());
+ }
+ }
+ result.appendFormat(" %s: %s\n",
+ toString(observable.type).c_str(), enabledEventsStr.c_str());
+ }
+ }
+ }
+
+ write(fd, result.string(), result.size());
+ return OK;
+}
+
+Status ResourceObserverService::registerObserver(
+ const std::shared_ptr<IResourceObserver>& in_observer,
+ const std::vector<MediaObservableFilter>& in_filters) {
+ // TODO(chz): Guard this by a permission.
+
+ ::ndk::SpAIBinder binder = in_observer->asBinder();
+
+ {
+ std::scoped_lock lock{mObserverLock};
+
+ if (mObserverInfoMap.find((uintptr_t)binder.get()) != mObserverInfoMap.end()) {
+ return Status::fromServiceSpecificError(ALREADY_EXISTS);
+ }
+
+ if (in_filters.empty()) {
+ return Status::fromServiceSpecificError(BAD_VALUE);
+ }
+
+ // Add observer info.
+ mObserverInfoMap.emplace((uintptr_t)binder.get(),
+ ObserverInfo{binder, in_observer, in_filters});
+
+ // Add observer to observable->subscribers map.
+ for (auto &filter : in_filters) {
+ for (auto &event : sEvents) {
+ if (!((uint64_t)filter.eventFilter & (uint64_t)event)) {
+ continue;
+ }
+ MediaObservableFilter key{filter.type, event};
+ mObservableToSubscribersMap[key].emplace((uintptr_t)binder.get(), in_observer);
+ }
+ }
+ }
+
+ // Add death binder and link.
+ uintptr_t cookie = (uintptr_t)binder.get();
+ {
+ std::scoped_lock lock{sDeathRecipientLock};
+ sDeathRecipientMap.emplace(
+ cookie, std::make_shared<DeathRecipient>(this, in_observer));
+ }
+
+ AIBinder_linkToDeath(binder.get(), mDeathRecipient.get(),
+ reinterpret_cast<void*>(cookie));
+
+ return Status::ok();
+}
+
+Status ResourceObserverService::unregisterObserver(
+ const std::shared_ptr<IResourceObserver>& in_observer) {
+ // TODO(chz): Guard this by a permission.
+
+ ::ndk::SpAIBinder binder = in_observer->asBinder();
+
+ {
+ std::scoped_lock lock{mObserverLock};
+
+ auto it = mObserverInfoMap.find((uintptr_t)binder.get());
+ if (it == mObserverInfoMap.end()) {
+ return Status::fromServiceSpecificError(NAME_NOT_FOUND);
+ }
+
+ // Remove observer from observable->subscribers map.
+ for (auto &filter : it->second.filters) {
+ for (auto &event : sEvents) {
+ if (!((uint64_t)filter.eventFilter & (uint64_t)event)) {
+ continue;
+ }
+ MediaObservableFilter key{filter.type, event};
+ mObservableToSubscribersMap[key].erase((uintptr_t)binder.get());
+
+ //Remove the entry if there's no more subscribers.
+ if (mObservableToSubscribersMap[key].empty()) {
+ mObservableToSubscribersMap.erase(key);
+ }
+ }
+ }
+
+ // Remove observer info.
+ mObserverInfoMap.erase(it);
+ }
+
+ // Unlink and remove death binder.
+ uintptr_t cookie = (uintptr_t)binder.get();
+ AIBinder_unlinkToDeath(binder.get(), mDeathRecipient.get(),
+ reinterpret_cast<void*>(cookie));
+
+ {
+ std::scoped_lock lock{sDeathRecipientLock};
+ sDeathRecipientMap.erase(cookie);
+ }
+
+ return Status::ok();
+}
+
+void ResourceObserverService::notifyObservers(
+ MediaObservableEvent event, int uid, int pid, const ResourceList &resources) {
+ struct CalleeInfo {
+ std::shared_ptr<IResourceObserver> observer;
+ std::vector<MediaObservableParcel> monitors;
+ };
+ // Build a consolidated list of observers to call with their respective observables.
+ std::map<uintptr_t, CalleeInfo> calleeList;
+
+ {
+ std::scoped_lock lock{mObserverLock};
+
+ for (auto &res : resources) {
+ // Skip if this resource doesn't map to any observable type.
+ MediaObservableType observableType = getObservableType(res.second);
+ if (observableType == MediaObservableType::kInvalid) {
+ continue;
+ }
+ MediaObservableFilter key{observableType, event};
+ // Skip if no one subscribed to this observable.
+ auto observableIt = mObservableToSubscribersMap.find(key);
+ if (observableIt == mObservableToSubscribersMap.end()) {
+ continue;
+ }
+ // Loop through all subsribers.
+ for (auto &subscriber : observableIt->second) {
+ auto calleeIt = calleeList.find(subscriber.first);
+ if (calleeIt == calleeList.end()) {
+ calleeList.emplace(subscriber.first, CalleeInfo{
+ subscriber.second, {{observableType, res.second.value}}});
+ } else {
+ calleeIt->second.monitors.push_back({observableType, res.second.value});
+ }
+ }
+ }
+ }
+
+ // Finally call the observers about the status change.
+ for (auto &calleeInfo : calleeList) {
+ calleeInfo.second.observer->onStatusChanged(
+ event, uid, pid, calleeInfo.second.monitors);
+ }
+}
+
+void ResourceObserverService::onResourceAdded(
+ int uid, int pid, const ResourceList &resources) {
+ notifyObservers(MediaObservableEvent::kBusy, uid, pid, resources);
+}
+
+void ResourceObserverService::onResourceRemoved(
+ int uid, int pid, const ResourceList &resources) {
+ notifyObservers(MediaObservableEvent::kIdle, uid, pid, resources);
+}
+
+} // namespace android