• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019, 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 <map>
18 #include <thread>
19 #include <vector>
20 
21 #include <stats_event.h>
22 #include <stats_pull_atom_callback.h>
23 
24 #include <aidl/android/os/BnPullAtomCallback.h>
25 #include <aidl/android/os/IPullAtomResultReceiver.h>
26 #include <aidl/android/os/IStatsd.h>
27 #include <aidl/android/util/StatsEventParcel.h>
28 #include <android/binder_auto_utils.h>
29 #include <android/binder_ibinder.h>
30 #include <android/binder_manager.h>
31 
32 using Status = ::ndk::ScopedAStatus;
33 using aidl::android::os::BnPullAtomCallback;
34 using aidl::android::os::IPullAtomResultReceiver;
35 using aidl::android::os::IStatsd;
36 using aidl::android::util::StatsEventParcel;
37 using ::ndk::SharedRefBase;
38 
39 struct AStatsEventList {
40     std::vector<AStatsEvent*> data;
41 };
42 
AStatsEventList_addStatsEvent(AStatsEventList * pull_data)43 AStatsEvent* AStatsEventList_addStatsEvent(AStatsEventList* pull_data) {
44     AStatsEvent* event = AStatsEvent_obtain();
45     pull_data->data.push_back(event);
46     return event;
47 }
48 
49 constexpr int64_t DEFAULT_COOL_DOWN_MILLIS = 1000LL;  // 1 second.
50 constexpr int64_t DEFAULT_TIMEOUT_MILLIS = 2000LL;    // 2 seconds.
51 
52 struct AStatsManager_PullAtomMetadata {
53     int64_t cool_down_millis;
54     int64_t timeout_millis;
55     std::vector<int32_t> additive_fields;
56 };
57 
AStatsManager_PullAtomMetadata_obtain()58 AStatsManager_PullAtomMetadata* AStatsManager_PullAtomMetadata_obtain() {
59     AStatsManager_PullAtomMetadata* metadata = new AStatsManager_PullAtomMetadata();
60     metadata->cool_down_millis = DEFAULT_COOL_DOWN_MILLIS;
61     metadata->timeout_millis = DEFAULT_TIMEOUT_MILLIS;
62     metadata->additive_fields = std::vector<int32_t>();
63     return metadata;
64 }
65 
AStatsManager_PullAtomMetadata_release(AStatsManager_PullAtomMetadata * metadata)66 void AStatsManager_PullAtomMetadata_release(AStatsManager_PullAtomMetadata* metadata) {
67     delete metadata;
68 }
69 
AStatsManager_PullAtomMetadata_setCoolDownMillis(AStatsManager_PullAtomMetadata * metadata,int64_t cool_down_millis)70 void AStatsManager_PullAtomMetadata_setCoolDownMillis(AStatsManager_PullAtomMetadata* metadata,
71                                                       int64_t cool_down_millis) {
72     metadata->cool_down_millis = cool_down_millis;
73 }
74 
AStatsManager_PullAtomMetadata_getCoolDownMillis(AStatsManager_PullAtomMetadata * metadata)75 int64_t AStatsManager_PullAtomMetadata_getCoolDownMillis(AStatsManager_PullAtomMetadata* metadata) {
76     return metadata->cool_down_millis;
77 }
78 
AStatsManager_PullAtomMetadata_setTimeoutMillis(AStatsManager_PullAtomMetadata * metadata,int64_t timeout_millis)79 void AStatsManager_PullAtomMetadata_setTimeoutMillis(AStatsManager_PullAtomMetadata* metadata,
80                                                      int64_t timeout_millis) {
81     metadata->timeout_millis = timeout_millis;
82 }
83 
AStatsManager_PullAtomMetadata_getTimeoutMillis(AStatsManager_PullAtomMetadata * metadata)84 int64_t AStatsManager_PullAtomMetadata_getTimeoutMillis(AStatsManager_PullAtomMetadata* metadata) {
85     return metadata->timeout_millis;
86 }
87 
AStatsManager_PullAtomMetadata_setAdditiveFields(AStatsManager_PullAtomMetadata * metadata,int32_t * additive_fields,int32_t num_fields)88 void AStatsManager_PullAtomMetadata_setAdditiveFields(AStatsManager_PullAtomMetadata* metadata,
89                                                       int32_t* additive_fields,
90                                                       int32_t num_fields) {
91     metadata->additive_fields.assign(additive_fields, additive_fields + num_fields);
92 }
93 
AStatsManager_PullAtomMetadata_getNumAdditiveFields(AStatsManager_PullAtomMetadata * metadata)94 int32_t AStatsManager_PullAtomMetadata_getNumAdditiveFields(
95         AStatsManager_PullAtomMetadata* metadata) {
96     return metadata->additive_fields.size();
97 }
98 
AStatsManager_PullAtomMetadata_getAdditiveFields(AStatsManager_PullAtomMetadata * metadata,int32_t * fields)99 void AStatsManager_PullAtomMetadata_getAdditiveFields(AStatsManager_PullAtomMetadata* metadata,
100                                                       int32_t* fields) {
101     std::copy(metadata->additive_fields.begin(), metadata->additive_fields.end(), fields);
102 }
103 
104 class StatsPullAtomCallbackInternal : public BnPullAtomCallback {
105   public:
StatsPullAtomCallbackInternal(const AStatsManager_PullAtomCallback callback,void * cookie,const int64_t coolDownMillis,const int64_t timeoutMillis,const std::vector<int32_t> additiveFields)106     StatsPullAtomCallbackInternal(const AStatsManager_PullAtomCallback callback, void* cookie,
107                                   const int64_t coolDownMillis, const int64_t timeoutMillis,
108                                   const std::vector<int32_t> additiveFields)
109         : mCallback(callback),
110           mCookie(cookie),
111           mCoolDownMillis(coolDownMillis),
112           mTimeoutMillis(timeoutMillis),
113           mAdditiveFields(additiveFields) {}
114 
onPullAtom(int32_t atomTag,const std::shared_ptr<IPullAtomResultReceiver> & resultReceiver)115     Status onPullAtom(int32_t atomTag,
116                       const std::shared_ptr<IPullAtomResultReceiver>& resultReceiver) override {
117         AStatsEventList statsEventList;
118         int successInt = mCallback(atomTag, &statsEventList, mCookie);
119         bool success = successInt == AStatsManager_PULL_SUCCESS;
120 
121         // Convert stats_events into StatsEventParcels.
122         std::vector<StatsEventParcel> parcels;
123 
124         // Resolves fuzz build failure in b/161575591.
125 #if defined(__ANDROID_APEX__) || defined(LIB_STATS_PULL_TESTS_FLAG)
126         for (int i = 0; i < statsEventList.data.size(); i++) {
127             size_t size;
128             uint8_t* buffer = AStatsEvent_getBuffer(statsEventList.data[i], &size);
129 
130             StatsEventParcel p;
131             // vector.assign() creates a copy, but this is inevitable unless
132             // stats_event.h/c uses a vector as opposed to a buffer.
133             p.buffer.assign(buffer, buffer + size);
134             parcels.push_back(std::move(p));
135         }
136 #endif
137 
138         Status status = resultReceiver->pullFinished(atomTag, success, parcels);
139         if (!status.isOk()) {
140             std::vector<StatsEventParcel> emptyParcels;
141             resultReceiver->pullFinished(atomTag, /*success=*/false, emptyParcels);
142         }
143         for (int i = 0; i < statsEventList.data.size(); i++) {
144             AStatsEvent_release(statsEventList.data[i]);
145         }
146         return Status::ok();
147     }
148 
getCoolDownMillis() const149     int64_t getCoolDownMillis() const { return mCoolDownMillis; }
getTimeoutMillis() const150     int64_t getTimeoutMillis() const { return mTimeoutMillis; }
getAdditiveFields() const151     const std::vector<int32_t>& getAdditiveFields() const { return mAdditiveFields; }
152 
153   private:
154     const AStatsManager_PullAtomCallback mCallback;
155     void* mCookie;
156     const int64_t mCoolDownMillis;
157     const int64_t mTimeoutMillis;
158     const std::vector<int32_t> mAdditiveFields;
159 };
160 
161 /**
162  * @brief pullAtomMutex is used to guard simultaneous access to mPullers from below threads
163  * Main thread
164  * - AStatsManager_setPullAtomCallback()
165  * - AStatsManager_clearPullAtomCallback()
166  * Binder thread:
167  * - StatsdProvider::binderDied()
168  */
169 static std::mutex pullAtomMutex;
170 
171 static std::map<int32_t, std::shared_ptr<StatsPullAtomCallbackInternal>> mPullers;
172 
173 class StatsdProvider {
174 public:
StatsdProvider()175     StatsdProvider() : deathRecipient(AIBinder_DeathRecipient_new(binderDied)) {
176     }
177 
~StatsdProvider()178     ~StatsdProvider() {
179         resetStatsService();
180     }
181 
getStatsService()182     std::shared_ptr<IStatsd> getStatsService() {
183         std::lock_guard<std::mutex> lock(statsdMutex);
184         if (!statsd) {
185             // Fetch statsd
186             ::ndk::SpAIBinder binder(AServiceManager_getService("stats"));
187             statsd = IStatsd::fromBinder(binder);
188             if (statsd) {
189                 AIBinder_linkToDeath(binder.get(), deathRecipient.get(), this);
190             }
191         }
192         return statsd;
193     }
194 
resetStatsService()195     void resetStatsService() {
196         std::lock_guard<std::mutex> lock(statsdMutex);
197         statsd = nullptr;
198     }
199 
binderDied(void * cookie)200     static void binderDied(void* cookie) {
201         StatsdProvider* statsProvider = static_cast<StatsdProvider*>(cookie);
202         statsProvider->resetStatsService();
203 
204         std::shared_ptr<IStatsd> statsService = statsProvider->getStatsService();
205         if (statsService == nullptr) {
206             return;
207         }
208 
209         // Since we do not want to make an IPC with the lock held, we first create a
210         // copy of the data with the lock held before iterating through the map.
211         std::map<int32_t, std::shared_ptr<StatsPullAtomCallbackInternal>> pullersCopy;
212         {
213             std::lock_guard<std::mutex> lock(pullAtomMutex);
214             pullersCopy = mPullers;
215         }
216         for (const auto& it : pullersCopy) {
217             statsService->registerNativePullAtomCallback(it.first, it.second->getCoolDownMillis(),
218                                                          it.second->getTimeoutMillis(),
219                                                          it.second->getAdditiveFields(), it.second);
220         }
221     }
222 
223 private:
224     /**
225      * @brief statsdMutex is used to guard simultaneous access to statsd from below threads:
226      * Work thread
227      * - registerStatsPullAtomCallbackBlocking()
228      * - unregisterStatsPullAtomCallbackBlocking()
229      * Binder thread:
230      * - StatsdProvider::binderDied()
231      */
232     std::mutex statsdMutex;
233     std::shared_ptr<IStatsd> statsd;
234     ::ndk::ScopedAIBinder_DeathRecipient deathRecipient;
235 };
236 
237 static std::shared_ptr<StatsdProvider> statsProvider = std::make_shared<StatsdProvider>();
238 
registerStatsPullAtomCallbackBlocking(int32_t atomTag,std::shared_ptr<StatsdProvider> statsProvider,std::shared_ptr<StatsPullAtomCallbackInternal> cb)239 void registerStatsPullAtomCallbackBlocking(int32_t atomTag,
240                                            std::shared_ptr<StatsdProvider> statsProvider,
241                                            std::shared_ptr<StatsPullAtomCallbackInternal> cb) {
242     const std::shared_ptr<IStatsd> statsService = statsProvider->getStatsService();
243     if (statsService == nullptr) {
244         // Statsd not available
245         return;
246     }
247 
248     statsService->registerNativePullAtomCallback(
249             atomTag, cb->getCoolDownMillis(), cb->getTimeoutMillis(), cb->getAdditiveFields(), cb);
250 }
251 
unregisterStatsPullAtomCallbackBlocking(int32_t atomTag,std::shared_ptr<StatsdProvider> statsProvider)252 void unregisterStatsPullAtomCallbackBlocking(int32_t atomTag,
253                                              std::shared_ptr<StatsdProvider> statsProvider) {
254     const std::shared_ptr<IStatsd> statsService = statsProvider->getStatsService();
255     if (statsService == nullptr) {
256         // Statsd not available
257         return;
258     }
259 
260     statsService->unregisterNativePullAtomCallback(atomTag);
261 }
262 
AStatsManager_setPullAtomCallback(int32_t atom_tag,AStatsManager_PullAtomMetadata * metadata,AStatsManager_PullAtomCallback callback,void * cookie)263 void AStatsManager_setPullAtomCallback(int32_t atom_tag, AStatsManager_PullAtomMetadata* metadata,
264                                        AStatsManager_PullAtomCallback callback, void* cookie) {
265     int64_t coolDownMillis =
266             metadata == nullptr ? DEFAULT_COOL_DOWN_MILLIS : metadata->cool_down_millis;
267     int64_t timeoutMillis = metadata == nullptr ? DEFAULT_TIMEOUT_MILLIS : metadata->timeout_millis;
268 
269     std::vector<int32_t> additiveFields;
270     if (metadata != nullptr) {
271         additiveFields = metadata->additive_fields;
272     }
273 
274     std::shared_ptr<StatsPullAtomCallbackInternal> callbackBinder =
275             SharedRefBase::make<StatsPullAtomCallbackInternal>(callback, cookie, coolDownMillis,
276                                                                timeoutMillis, additiveFields);
277 
278     {
279         std::lock_guard<std::mutex> lg(pullAtomMutex);
280         // Always add to the map. If statsd is dead, we will add them when it comes back.
281         mPullers[atom_tag] = callbackBinder;
282     }
283 
284     std::thread registerThread(registerStatsPullAtomCallbackBlocking, atom_tag, statsProvider,
285                                callbackBinder);
286     registerThread.detach();
287 }
288 
AStatsManager_clearPullAtomCallback(int32_t atom_tag)289 void AStatsManager_clearPullAtomCallback(int32_t atom_tag) {
290     {
291         std::lock_guard<std::mutex> lg(pullAtomMutex);
292         // Always remove the puller from our map.
293         // If statsd is down, we will not register it when it comes back.
294         mPullers.erase(atom_tag);
295     }
296 
297     std::thread unregisterThread(unregisterStatsPullAtomCallbackBlocking, atom_tag, statsProvider);
298     unregisterThread.detach();
299 }
300