1 /*
2 * Copyright (C) 2023, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <aidl/android/os/BnStatsSubscriptionCallback.h>
18 #include <aidl/android/os/IStatsd.h>
19 #include <aidl/android/os/StatsSubscriptionCallbackReason.h>
20 #include <android/binder_auto_utils.h>
21 #include <stats_provider.h>
22 #include <stats_subscription.h>
23
24 #include <atomic>
25 #include <map>
26 #include <vector>
27
28 using Status = ::ndk::ScopedAStatus;
29 using aidl::android::os::BnStatsSubscriptionCallback;
30 using aidl::android::os::IStatsd;
31 using aidl::android::os::StatsSubscriptionCallbackReason;
32 using ::ndk::SharedRefBase;
33
34 class Subscription;
35
36 // Mutex for accessing subscriptions map.
37 static std::mutex subscriptionsMutex;
38
39 // TODO(b/271039569): Store subscriptions in a singleton object.
40 // Added subscriptions keyed by their subscription ID.
41 static std::map</* subscription ID */ int32_t, std::shared_ptr<Subscription>> subscriptions;
42
43 class Subscription : public BnStatsSubscriptionCallback {
44 public:
Subscription(const int32_t subscriptionId,const std::vector<uint8_t> & subscriptionConfig,const AStatsManager_SubscriptionCallback callback,void * cookie)45 Subscription(const int32_t subscriptionId, const std::vector<uint8_t>& subscriptionConfig,
46 const AStatsManager_SubscriptionCallback callback, void* cookie)
47 : mSubscriptionId(subscriptionId),
48 mSubscriptionParamsBytes(subscriptionConfig),
49 mCallback(callback),
50 mCookie(cookie) {
51 }
52
onSubscriptionData(const StatsSubscriptionCallbackReason reason,const std::vector<uint8_t> & subscriptionPayload)53 Status onSubscriptionData(const StatsSubscriptionCallbackReason reason,
54 const std::vector<uint8_t>& subscriptionPayload) override {
55 std::vector<uint8_t> mutablePayload = subscriptionPayload;
56 mCallback(mSubscriptionId, static_cast<AStatsManager_SubscriptionCallbackReason>(reason),
57 mutablePayload.data(), mutablePayload.size(), mCookie);
58
59 std::shared_ptr<Subscription> thisSubscription;
60 if (reason == StatsSubscriptionCallbackReason::SUBSCRIPTION_ENDED) {
61 std::lock_guard<std::mutex> lock(subscriptionsMutex);
62
63 auto subscriptionsIt = subscriptions.find(mSubscriptionId);
64 if (subscriptionsIt != subscriptions.end()) {
65 // Ensure this subscription's refcount doesn't hit 0 when we erase it from the
66 // subscriptions map by adding a local reference here.
67 thisSubscription = subscriptionsIt->second;
68
69 subscriptions.erase(subscriptionsIt);
70 }
71 }
72
73 return Status::ok();
74 }
75
getSubscriptionParamsBytes() const76 const std::vector<uint8_t>& getSubscriptionParamsBytes() const {
77 return mSubscriptionParamsBytes;
78 }
79
80 private:
81 const int32_t mSubscriptionId;
82 const std::vector<uint8_t> mSubscriptionParamsBytes;
83 const AStatsManager_SubscriptionCallback mCallback;
84 void* mCookie;
85 };
86
87 // forward declare so it can be referenced in StatsProvider constructor.
88 static void onStatsBinderRestart();
89
90 static std::shared_ptr<StatsProvider> statsProvider =
91 std::make_shared<StatsProvider>(onStatsBinderRestart);
92
onStatsBinderRestart()93 static void onStatsBinderRestart() {
94 const std::shared_ptr<IStatsd> statsService = statsProvider->getStatsService();
95 if (statsService == nullptr) {
96 return;
97 }
98
99 // Since we do not want to make an IPC with the lock held, we first create a
100 // copy of the data with the lock held before iterating through the map.
101 std::map<int32_t, std::shared_ptr<Subscription>> subscriptionsCopy;
102 {
103 std::lock_guard<std::mutex> lock(subscriptionsMutex);
104 subscriptionsCopy = subscriptions;
105 }
106 for (const auto& [_, subscription] : subscriptionsCopy) {
107 statsService->addSubscription(subscription->getSubscriptionParamsBytes(), subscription);
108 }
109 }
110
getNextSubscriptionId()111 static int32_t getNextSubscriptionId() {
112 static std::atomic_int32_t nextSubscriptionId(0);
113 return ++nextSubscriptionId;
114 }
115
getBinderCallbackForSubscription(const int32_t subscription_id)116 static std::shared_ptr<Subscription> getBinderCallbackForSubscription(
117 const int32_t subscription_id) {
118 std::lock_guard<std::mutex> lock(subscriptionsMutex);
119 auto subscriptionsIt = subscriptions.find(subscription_id);
120 if (subscriptionsIt == subscriptions.end()) {
121 return nullptr;
122 }
123 return subscriptionsIt->second;
124 }
125
AStatsManager_addSubscription(const uint8_t * subscription_config,const size_t num_bytes,const AStatsManager_SubscriptionCallback callback,void * cookie)126 int32_t AStatsManager_addSubscription(const uint8_t* subscription_config, const size_t num_bytes,
127 const AStatsManager_SubscriptionCallback callback,
128 void* cookie) {
129 const std::vector<uint8_t> subscriptionConfig(subscription_config,
130 subscription_config + num_bytes);
131 const int32_t subscriptionId(getNextSubscriptionId());
132 std::shared_ptr<Subscription> subscription =
133 SharedRefBase::make<Subscription>(subscriptionId, subscriptionConfig, callback, cookie);
134
135 {
136 std::lock_guard<std::mutex> lock(subscriptionsMutex);
137
138 subscriptions[subscriptionId] = subscription;
139 }
140
141 // TODO(b/270648168): Queue the binder call to not block on binder
142 const std::shared_ptr<IStatsd> statsService = statsProvider->getStatsService();
143 if (statsService != nullptr) {
144 statsService->addSubscription(subscriptionConfig, subscription);
145 }
146
147 return subscriptionId;
148 }
149
AStatsManager_removeSubscription(const int32_t subscription_id)150 void AStatsManager_removeSubscription(const int32_t subscription_id) {
151 std::shared_ptr<Subscription> subscription = getBinderCallbackForSubscription(subscription_id);
152 if (subscription == nullptr) {
153 return;
154 }
155
156 // TODO(b/270648168): Queue the binder call to not block on binder
157 const std::shared_ptr<IStatsd> statsService = statsProvider->getStatsService();
158 if (statsService == nullptr) {
159 // Statsd not available.
160 // TODO(b/270656443): keep track of removeSubscription request and make the IPC call when
161 // statsd binder comes back up.
162 return;
163 }
164 statsService->removeSubscription(subscription);
165 }
166
AStatsManager_flushSubscription(const int32_t subscription_id)167 void AStatsManager_flushSubscription(const int32_t subscription_id) {
168 std::shared_ptr<Subscription> subscription = getBinderCallbackForSubscription(subscription_id);
169 if (subscription == nullptr) {
170 return;
171 }
172
173 // TODO(b/270648168): Queue the binder call to not block on binder
174 const std::shared_ptr<IStatsd> statsService = statsProvider->getStatsService();
175 if (statsService == nullptr) {
176 // Statsd not available.
177 // TODO(b/270656443): keep track of flushSubscription request and make the IPC call when
178 // statsd binder comes back up.
179 return;
180 }
181
182 // TODO(b/273649282): Ensure the subscription is cleared in case the final Binder data
183 // callback fails.
184 statsService->flushSubscription(subscription);
185 }
186