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 static const int64_t DEFAULT_COOL_DOWN_MILLIS = 1000LL; // 1 second.
50 static const 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 static std::mutex pullAtomMutex;
162 static std::shared_ptr<IStatsd> sStatsd = nullptr;
163
164 static std::map<int32_t, std::shared_ptr<StatsPullAtomCallbackInternal>> mPullers;
165 static std::shared_ptr<IStatsd> getStatsService();
166
binderDied(void *)167 static void binderDied(void* /*cookie*/) {
168 {
169 std::lock_guard<std::mutex> lock(pullAtomMutex);
170 sStatsd = nullptr;
171 }
172
173 std::shared_ptr<IStatsd> statsService = getStatsService();
174 if (statsService == nullptr) {
175 return;
176 }
177
178 // Since we do not want to make an IPC with the lock held, we first create a
179 // copy of the data with the lock held before iterating through the map.
180 std::map<int32_t, std::shared_ptr<StatsPullAtomCallbackInternal>> pullersCopy;
181 {
182 std::lock_guard<std::mutex> lock(pullAtomMutex);
183 pullersCopy = mPullers;
184 }
185 for (const auto& it : pullersCopy) {
186 statsService->registerNativePullAtomCallback(it.first, it.second->getCoolDownMillis(),
187 it.second->getTimeoutMillis(),
188 it.second->getAdditiveFields(), it.second);
189 }
190 }
191
192 static ::ndk::ScopedAIBinder_DeathRecipient sDeathRecipient(
193 AIBinder_DeathRecipient_new(binderDied));
194
getStatsService()195 static std::shared_ptr<IStatsd> getStatsService() {
196 std::lock_guard<std::mutex> lock(pullAtomMutex);
197 if (!sStatsd) {
198 // Fetch statsd
199 ::ndk::SpAIBinder binder(AServiceManager_getService("stats"));
200 sStatsd = IStatsd::fromBinder(binder);
201 if (sStatsd) {
202 AIBinder_linkToDeath(binder.get(), sDeathRecipient.get(), /*cookie=*/nullptr);
203 }
204 }
205 return sStatsd;
206 }
207
registerStatsPullAtomCallbackBlocking(int32_t atomTag,std::shared_ptr<StatsPullAtomCallbackInternal> cb)208 void registerStatsPullAtomCallbackBlocking(int32_t atomTag,
209 std::shared_ptr<StatsPullAtomCallbackInternal> cb) {
210 const std::shared_ptr<IStatsd> statsService = getStatsService();
211 if (statsService == nullptr) {
212 // Statsd not available
213 return;
214 }
215
216 statsService->registerNativePullAtomCallback(
217 atomTag, cb->getCoolDownMillis(), cb->getTimeoutMillis(), cb->getAdditiveFields(), cb);
218 }
219
unregisterStatsPullAtomCallbackBlocking(int32_t atomTag)220 void unregisterStatsPullAtomCallbackBlocking(int32_t atomTag) {
221 const std::shared_ptr<IStatsd> statsService = getStatsService();
222 if (statsService == nullptr) {
223 // Statsd not available
224 return;
225 }
226
227 statsService->unregisterNativePullAtomCallback(atomTag);
228 }
229
AStatsManager_setPullAtomCallback(int32_t atom_tag,AStatsManager_PullAtomMetadata * metadata,AStatsManager_PullAtomCallback callback,void * cookie)230 void AStatsManager_setPullAtomCallback(int32_t atom_tag, AStatsManager_PullAtomMetadata* metadata,
231 AStatsManager_PullAtomCallback callback, void* cookie) {
232 int64_t coolDownMillis =
233 metadata == nullptr ? DEFAULT_COOL_DOWN_MILLIS : metadata->cool_down_millis;
234 int64_t timeoutMillis = metadata == nullptr ? DEFAULT_TIMEOUT_MILLIS : metadata->timeout_millis;
235
236 std::vector<int32_t> additiveFields;
237 if (metadata != nullptr) {
238 additiveFields = metadata->additive_fields;
239 }
240
241 std::shared_ptr<StatsPullAtomCallbackInternal> callbackBinder =
242 SharedRefBase::make<StatsPullAtomCallbackInternal>(callback, cookie, coolDownMillis,
243 timeoutMillis, additiveFields);
244
245 {
246 std::lock_guard<std::mutex> lg(pullAtomMutex);
247 // Always add to the map. If statsd is dead, we will add them when it comes back.
248 mPullers[atom_tag] = callbackBinder;
249 }
250
251 std::thread registerThread(registerStatsPullAtomCallbackBlocking, atom_tag, callbackBinder);
252 registerThread.detach();
253 }
254
AStatsManager_clearPullAtomCallback(int32_t atom_tag)255 void AStatsManager_clearPullAtomCallback(int32_t atom_tag) {
256 {
257 std::lock_guard<std::mutex> lg(pullAtomMutex);
258 // Always remove the puller from our map.
259 // If statsd is down, we will not register it when it comes back.
260 mPullers.erase(atom_tag);
261 }
262 std::thread unregisterThread(unregisterStatsPullAtomCallbackBlocking, atom_tag);
263 unregisterThread.detach();
264 }
265