blob: 3bc7e793ea67f3d57a593fa1ca0e9672817534ba [file] [log] [blame]
/*
* 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 <aidl/android/os/BnStatsSubscriptionCallback.h>
#include <aidl/android/os/IStatsd.h>
#include <aidl/android/os/StatsSubscriptionCallbackReason.h>
#include <android/binder_auto_utils.h>
#include <stats_provider.h>
#include <stats_subscription.h>
#include <atomic>
#include <map>
#include <vector>
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</* subscription ID */ int32_t, std::shared_ptr<Subscription>> subscriptions;
class Subscription : public BnStatsSubscriptionCallback {
public:
Subscription(const int32_t subscriptionId, const std::vector<uint8_t>& subscriptionConfig,
const AStatsManager_SubscriptionCallback callback, void* cookie)
: mSubscriptionId(subscriptionId),
mSubscriptionParamsBytes(subscriptionConfig),
mCallback(callback),
mCookie(cookie) {
}
Status onSubscriptionData(const StatsSubscriptionCallbackReason reason,
const std::vector<uint8_t>& subscriptionPayload) override {
std::vector<uint8_t> mutablePayload = subscriptionPayload;
mCallback(mSubscriptionId, static_cast<AStatsManager_SubscriptionCallbackReason>(reason),
mutablePayload.data(), mutablePayload.size(), mCookie);
std::shared_ptr<Subscription> thisSubscription;
if (reason == StatsSubscriptionCallbackReason::SUBSCRIPTION_ENDED) {
std::lock_guard<std::mutex> 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<uint8_t>& getSubscriptionParamsBytes() const {
return mSubscriptionParamsBytes;
}
private:
const int32_t mSubscriptionId;
const std::vector<uint8_t> 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> statsProvider =
std::make_shared<StatsProvider>(onStatsBinderRestart);
static void onStatsBinderRestart() {
const std::shared_ptr<IStatsd> 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<int32_t, std::shared_ptr<Subscription>> subscriptionsCopy;
{
std::lock_guard<std::mutex> 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<Subscription> getBinderCallbackForSubscription(
const int32_t subscription_id) {
std::lock_guard<std::mutex> 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<uint8_t> subscriptionConfig(subscription_config,
subscription_config + num_bytes);
const int32_t subscriptionId(getNextSubscriptionId());
std::shared_ptr<Subscription> subscription =
SharedRefBase::make<Subscription>(subscriptionId, subscriptionConfig, callback, cookie);
{
std::lock_guard<std::mutex> lock(subscriptionsMutex);
subscriptions[subscriptionId] = subscription;
}
// TODO(b/270648168): Queue the binder call to not block on binder
const std::shared_ptr<IStatsd> statsService = statsProvider->getStatsService();
if (statsService != nullptr) {
statsService->addSubscription(subscriptionConfig, subscription);
}
return subscriptionId;
}
void AStatsManager_removeSubscription(const int32_t subscription_id) {
std::shared_ptr<Subscription> subscription = getBinderCallbackForSubscription(subscription_id);
if (subscription == nullptr) {
return;
}
// TODO(b/270648168): Queue the binder call to not block on binder
const std::shared_ptr<IStatsd> 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> subscription = getBinderCallbackForSubscription(subscription_id);
if (subscription == nullptr) {
return;
}
// TODO(b/270648168): Queue the binder call to not block on binder
const std::shared_ptr<IStatsd> 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);
}