/* * Copyright (C) 2023, 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. */ #include #include #include #include #include #include #include #include #include using Status = ::ndk::ScopedAStatus; using aidl::android::os::BnStatsSubscriptionCallback; using aidl::android::os::IStatsd; using aidl::android::os::StatsSubscriptionCallbackReason; using ::ndk::SharedRefBase; class Subscription; // Mutex for accessing subscriptions map. static std::mutex subscriptionsMutex; // TODO(b/271039569): Store subscriptions in a singleton object. // Added subscriptions keyed by their subscription ID. static std::map> subscriptions; class Subscription : public BnStatsSubscriptionCallback { public: Subscription(const int32_t subscriptionId, const std::vector& subscriptionConfig, const AStatsManager_SubscriptionCallback callback, void* cookie) : mSubscriptionId(subscriptionId), mSubscriptionParamsBytes(subscriptionConfig), mCallback(callback), mCookie(cookie) { } Status onSubscriptionData(const StatsSubscriptionCallbackReason reason, const std::vector& subscriptionPayload) override { std::vector mutablePayload = subscriptionPayload; mCallback(mSubscriptionId, static_cast(reason), mutablePayload.data(), mutablePayload.size(), mCookie); std::shared_ptr thisSubscription; if (reason == StatsSubscriptionCallbackReason::SUBSCRIPTION_ENDED) { std::lock_guard lock(subscriptionsMutex); auto subscriptionsIt = subscriptions.find(mSubscriptionId); if (subscriptionsIt != subscriptions.end()) { // Ensure this subscription's refcount doesn't hit 0 when we erase it from the // subscriptions map by adding a local reference here. thisSubscription = subscriptionsIt->second; subscriptions.erase(subscriptionsIt); } } return Status::ok(); } const std::vector& getSubscriptionParamsBytes() const { return mSubscriptionParamsBytes; } private: const int32_t mSubscriptionId; const std::vector mSubscriptionParamsBytes; const AStatsManager_SubscriptionCallback mCallback; void* mCookie; }; // forward declare so it can be referenced in StatsProvider constructor. static void onStatsBinderRestart(); static std::shared_ptr statsProvider = std::make_shared(onStatsBinderRestart); static void onStatsBinderRestart() { const std::shared_ptr statsService = statsProvider->getStatsService(); if (statsService == nullptr) { return; } // Since we do not want to make an IPC with the lock held, we first create a // copy of the data with the lock held before iterating through the map. std::map> subscriptionsCopy; { std::lock_guard lock(subscriptionsMutex); subscriptionsCopy = subscriptions; } for (const auto& [_, subscription] : subscriptionsCopy) { statsService->addSubscription(subscription->getSubscriptionParamsBytes(), subscription); } } static int32_t getNextSubscriptionId() { static std::atomic_int32_t nextSubscriptionId(0); return ++nextSubscriptionId; } static std::shared_ptr getBinderCallbackForSubscription( const int32_t subscription_id) { std::lock_guard lock(subscriptionsMutex); auto subscriptionsIt = subscriptions.find(subscription_id); if (subscriptionsIt == subscriptions.end()) { return nullptr; } return subscriptionsIt->second; } int32_t AStatsManager_addSubscription(const uint8_t* subscription_config, const size_t num_bytes, const AStatsManager_SubscriptionCallback callback, void* cookie) { const std::vector subscriptionConfig(subscription_config, subscription_config + num_bytes); const int32_t subscriptionId(getNextSubscriptionId()); std::shared_ptr subscription = SharedRefBase::make(subscriptionId, subscriptionConfig, callback, cookie); { std::lock_guard lock(subscriptionsMutex); subscriptions[subscriptionId] = subscription; } // TODO(b/270648168): Queue the binder call to not block on binder const std::shared_ptr statsService = statsProvider->getStatsService(); if (statsService != nullptr) { statsService->addSubscription(subscriptionConfig, subscription); } return subscriptionId; } void AStatsManager_removeSubscription(const int32_t subscription_id) { std::shared_ptr subscription = getBinderCallbackForSubscription(subscription_id); if (subscription == nullptr) { return; } // TODO(b/270648168): Queue the binder call to not block on binder const std::shared_ptr statsService = statsProvider->getStatsService(); if (statsService == nullptr) { // Statsd not available. // TODO(b/270656443): keep track of removeSubscription request and make the IPC call when // statsd binder comes back up. return; } statsService->removeSubscription(subscription); } void AStatsManager_flushSubscription(const int32_t subscription_id) { std::shared_ptr subscription = getBinderCallbackForSubscription(subscription_id); if (subscription == nullptr) { return; } // TODO(b/270648168): Queue the binder call to not block on binder const std::shared_ptr statsService = statsProvider->getStatsService(); if (statsService == nullptr) { // Statsd not available. // TODO(b/270656443): keep track of flushSubscription request and make the IPC call when // statsd binder comes back up. return; } // TODO(b/273649282): Ensure the subscription is cleared in case the final Binder data // callback fails. statsService->flushSubscription(subscription); }