/** * * 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 #include #include #include #include #include #include "ResourceObserverService.h" 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 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 > ResourceObserverService::sDeathRecipientMap; struct ResourceObserverService::DeathRecipient { DeathRecipient(ResourceObserverService* _service, const std::shared_ptr& _observer) : service(_service), observer(_observer) {} ~DeathRecipient() {} void binderDied() { if (service != nullptr) { service->unregisterObserver(observer); } } ResourceObserverService* service; std::shared_ptr observer; }; // static void ResourceObserverService::BinderDiedCallback(void* cookie) { uintptr_t id = reinterpret_cast(cookie); ALOGW("Observer %lld is dead", (long long)id); std::shared_ptr 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::instantiate() { std::shared_ptr observerService = ::ndk::SharedRefBase::make(); 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& in_observer, const std::vector& in_filters) { if ((getpid() != AIBinder_getCallingPid()) && checkCallingPermission( String16("android.permission.REGISTER_MEDIA_RESOURCE_OBSERVER")) == false) { ALOGE("Permission Denial: " "can't registerObserver from pid=%d, uid=%d\n", AIBinder_getCallingPid(), AIBinder_getCallingUid()); return Status::fromServiceSpecificError(PERMISSION_DENIED); } if (in_observer == nullptr) { return Status::fromServiceSpecificError(BAD_VALUE); } ::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(this, in_observer)); } AIBinder_linkToDeath(binder.get(), mDeathRecipient.get(), reinterpret_cast(cookie)); return Status::ok(); } Status ResourceObserverService::unregisterObserver( const std::shared_ptr& in_observer) { if ((getpid() != AIBinder_getCallingPid()) && checkCallingPermission( String16("android.permission.REGISTER_MEDIA_RESOURCE_OBSERVER")) == false) { ALOGE("Permission Denial: " "can't unregisterObserver from pid=%d, uid=%d\n", AIBinder_getCallingPid(), AIBinder_getCallingUid()); return Status::fromServiceSpecificError(PERMISSION_DENIED); } if (in_observer == nullptr) { return Status::fromServiceSpecificError(BAD_VALUE); } ::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(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 observer; std::vector monitors; }; // Build a consolidated list of observers to call with their respective observables. std::map 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