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 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 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 66 void AStatsManager_PullAtomMetadata_release(AStatsManager_PullAtomMetadata* metadata) { 67 delete metadata; 68 } 69 70 void AStatsManager_PullAtomMetadata_setCoolDownMillis(AStatsManager_PullAtomMetadata* metadata, 71 int64_t cool_down_millis) { 72 metadata->cool_down_millis = cool_down_millis; 73 } 74 75 int64_t AStatsManager_PullAtomMetadata_getCoolDownMillis(AStatsManager_PullAtomMetadata* metadata) { 76 return metadata->cool_down_millis; 77 } 78 79 void AStatsManager_PullAtomMetadata_setTimeoutMillis(AStatsManager_PullAtomMetadata* metadata, 80 int64_t timeout_millis) { 81 metadata->timeout_millis = timeout_millis; 82 } 83 84 int64_t AStatsManager_PullAtomMetadata_getTimeoutMillis(AStatsManager_PullAtomMetadata* metadata) { 85 return metadata->timeout_millis; 86 } 87 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 94 int32_t AStatsManager_PullAtomMetadata_getNumAdditiveFields( 95 AStatsManager_PullAtomMetadata* metadata) { 96 return metadata->additive_fields.size(); 97 } 98 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: 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 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 149 int64_t getCoolDownMillis() const { return mCoolDownMillis; } 150 int64_t getTimeoutMillis() const { return mTimeoutMillis; } 151 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 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 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 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 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 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 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