1 /*
2 * Copyright (C) 2021 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 "StatsBase.h"
18
19 #include <aidl/android/frameworks/stats/IStats.h>
20 #include <android/binder_manager.h>
21 #include <hardware/google/pixel/pixelstats/pixelatoms.pb.h>
22 #include <log/log.h>
23 #include <utils/Trace.h>
24
25 #include <chrono>
26 #include <sstream>
27
28 using ::aidl::android::frameworks::stats::IStats;
29 using ::aidl::android::frameworks::stats::VendorAtom;
30 using ::aidl::android::frameworks::stats::VendorAtomValue;
31
32 namespace PixelAtoms = ::android::hardware::google::pixel::PixelAtoms;
33
34 #ifndef ARRAY_SIZE
35 #define ARRAY_SIZE(x) (sizeof((x)) / sizeof((x)[0]))
36 #endif
37
38 #ifdef TRACE_STATS
39 static const char *kAtomLookup[] = {"HAPTICS_PLAYCOUNTS", "HAPTICS_LATENCIES", "HAPTICS_ERRORS",
40 "INVALID"};
41
atomToString(uint32_t atomId)42 const char *atomToString(uint32_t atomId) {
43 switch (atomId) {
44 case PixelAtoms::Atom::kVibratorPlaycountReported:
45 return kAtomLookup[0];
46 break;
47 case PixelAtoms::Atom::kVibratorLatencyReported:
48 return kAtomLookup[1];
49 break;
50 case PixelAtoms::Atom::kVibratorErrorsReported:
51 return kAtomLookup[2];
52 break;
53 default:
54 return kAtomLookup[ARRAY_SIZE(kAtomLookup) - 1];
55 break;
56 }
57 }
58
59 #define STATS_TRACE(...) \
60 ATRACE_NAME(__func__); \
61 ALOGD(__VA_ARGS__)
62 #else
63 #define STATS_TRACE(...) ATRACE_NAME(__func__)
64 #define atomToString(x)
65 #endif
66
67 namespace aidl {
68 namespace android {
69 namespace hardware {
70 namespace vibrator {
71
72 #ifdef FAST_LOG
73 static constexpr auto UPLOAD_INTERVAL = std::chrono::minutes(1);
74 #else
75 static constexpr auto UPLOAD_INTERVAL = std::chrono::hours(24);
76 #endif
77
reportVendorAtom(const std::shared_ptr<IStats> & statsClient,const VendorAtom & atom)78 static void reportVendorAtom(const std::shared_ptr<IStats> &statsClient, const VendorAtom &atom) {
79 STATS_TRACE(" reportVendorAtom(statsClient, atom: %s)", atomToString(atom.atomId));
80 const ndk::ScopedAStatus status = statsClient->reportVendorAtom(atom);
81 if (status.isOk()) {
82 ALOGI("Vendor atom [id = %d] reported.", atom.atomId);
83 } else {
84 ALOGE("Failed to report atom [id = %d].", atom.atomId);
85 }
86 }
87
dumpData(const std::vector<int32_t> & data)88 static std::string dumpData(const std::vector<int32_t> &data) {
89 std::stringstream stream;
90 for (auto datum : data) {
91 stream << " " << datum;
92 }
93 return stream.str();
94 }
95
StatsBase(const std::string & instance)96 StatsBase::StatsBase(const std::string &instance)
97 : mReporterThread([this]() { runReporterThread(); }),
98 kStatsInstanceName(std::string() + IStats::descriptor + "/" + instance) {}
99
~StatsBase()100 StatsBase::~StatsBase() {}
101
debug(int fd)102 void StatsBase::debug(int fd) {
103 STATS_TRACE("debug(fd: %d)", fd);
104
105 dprintf(fd, "Stats:\n");
106 {
107 std::scoped_lock<std::mutex> lock(mDataAccess);
108 dprintf(fd, " Waveform Counts:%s\n", dumpData(mWaveformCounts).c_str());
109 dprintf(fd, " Duration Counts:%s\n", dumpData(mDurationCounts).c_str());
110 dprintf(fd, " Min Latencies:%s\n", dumpData(mMinLatencies).c_str());
111 dprintf(fd, " Max Latencies:%s\n", dumpData(mMaxLatencies).c_str());
112 dprintf(fd, " Latency Totals:%s\n", dumpData(mLatencyTotals).c_str());
113 dprintf(fd, " Latency Counts:%s\n", dumpData(mLatencyCounts).c_str());
114 dprintf(fd, " Error Counts: %s\n", dumpData(mErrorCounts).c_str());
115 }
116 }
117
reportVendorAtomAsync(const VendorAtom & atom)118 void StatsBase::reportVendorAtomAsync(const VendorAtom &atom) {
119 STATS_TRACE("reportVendorAtomAsync(atom: %s)", atomToString(atom.atomId));
120 std::scoped_lock<std::mutex> lock(mAtomQueueAccess);
121 mAtomQueue.push_back(atom);
122 mAtomQueueUpdated.notify_all();
123 }
124
uploadDiagnostics()125 void StatsBase::uploadDiagnostics() {
126 STATS_TRACE("uploadDiagnostics()");
127 uploadPlaycountAtoms();
128 uploadLatencyAtoms();
129 uploadErrorAtoms();
130 }
131
waitForStatsService() const132 void StatsBase::waitForStatsService() const {
133 STATS_TRACE("waitForStatsService()");
134 if (!AServiceManager_isDeclared(kStatsInstanceName.c_str())) {
135 ALOGE("IStats service '%s' is not registered.", kStatsInstanceName.c_str());
136 return;
137 }
138
139 ALOGI("Waiting for IStats service '%s' to come up.", kStatsInstanceName.c_str());
140 const std::shared_ptr<IStats> statsClient = IStats::fromBinder(
141 ndk::SpAIBinder(AServiceManager_waitForService(kStatsInstanceName.c_str())));
142 if (!statsClient) {
143 ALOGE("Failed to get IStats service '%s'.", kStatsInstanceName.c_str());
144 return;
145 }
146 ALOGI("IStats service online.");
147 }
148
runReporterThread()149 void StatsBase::runReporterThread() {
150 STATS_TRACE("runReporterThread()");
151 using clock = std::chrono::steady_clock;
152 auto nextUpload = clock::now() + UPLOAD_INTERVAL;
153 auto status = std::cv_status::no_timeout;
154
155 waitForStatsService();
156
157 while (!mTerminateReporterThread) {
158 drainAtomQueue();
159 {
160 std::unique_lock<std::mutex> lock(mAtomQueueAccess);
161 if (!mAtomQueue.empty())
162 continue;
163 status = mAtomQueueUpdated.wait_until(lock, nextUpload);
164 }
165
166 if (status == std::cv_status::timeout) {
167 nextUpload = clock::now() + UPLOAD_INTERVAL;
168 uploadDiagnostics();
169 }
170 }
171 }
172
drainAtomQueue()173 void StatsBase::drainAtomQueue() {
174 STATS_TRACE("drainAtomQueue()");
175 std::vector<VendorAtom> tempQueue;
176 {
177 std::unique_lock<std::mutex> lock(mAtomQueueAccess);
178 std::swap(mAtomQueue, tempQueue);
179 }
180
181 std::shared_ptr<IStats> statsClient = IStats::fromBinder(
182 ndk::SpAIBinder(AServiceManager_waitForService(kStatsInstanceName.c_str())));
183 if (!statsClient) {
184 ALOGE("Failed to get IStats service. Atoms are dropped.");
185 return;
186 }
187
188 for (const VendorAtom &atom : tempQueue) {
189 reportVendorAtom(statsClient, atom);
190 }
191 }
192
uploadPlaycountAtoms()193 void StatsBase::uploadPlaycountAtoms() {
194 STATS_TRACE("uploadPlaycountAtoms()");
195 VendorAtom playcountAtom = vibratorPlaycountAtom();
196 reportVendorAtomAsync(playcountAtom);
197 clearData(&mWaveformCounts);
198 clearData(&mDurationCounts);
199 }
200
uploadLatencyAtoms()201 void StatsBase::uploadLatencyAtoms() {
202 STATS_TRACE("uploadLatencyAtoms()");
203 VendorAtom latencyAtom = vibratorLatencyAtom();
204 reportVendorAtomAsync(latencyAtom);
205 clearData(&mMinLatencies);
206 clearData(&mMaxLatencies);
207 clearData(&mLatencyTotals);
208 clearData(&mLatencyCounts);
209 }
210
uploadErrorAtoms()211 void StatsBase::uploadErrorAtoms() {
212 STATS_TRACE("uploadErrorAtoms()");
213 VendorAtom errorAtom = vibratorErrorAtom();
214 reportVendorAtomAsync(errorAtom);
215 clearData(&mErrorCounts);
216 }
217
clearData(std::vector<int32_t> * data)218 void StatsBase::clearData(std::vector<int32_t> *data) {
219 STATS_TRACE("clearData(data)");
220 if (data) {
221 std::scoped_lock<std::mutex> lock(mDataAccess);
222 std::fill((*data).begin(), (*data).end(), 0);
223 }
224 }
225
vibratorPlaycountAtom()226 VendorAtom StatsBase::vibratorPlaycountAtom() {
227 STATS_TRACE("vibratorPlaycountAtom()");
228 std::vector<VendorAtomValue> values(2);
229
230 {
231 std::scoped_lock<std::mutex> lock(mDataAccess);
232 values[0].set<VendorAtomValue::repeatedIntValue>(mWaveformCounts);
233 values[1].set<VendorAtomValue::repeatedIntValue>(mDurationCounts);
234 }
235
236 return VendorAtom{
237 .reverseDomainName = "",
238 .atomId = PixelAtoms::Atom::kVibratorPlaycountReported,
239 .values = std::move(values),
240 };
241 }
242
vibratorLatencyAtom()243 VendorAtom StatsBase::vibratorLatencyAtom() {
244 STATS_TRACE("vibratorLatencyAtom()");
245 std::vector<VendorAtomValue> values(3);
246 std::vector<int32_t> avgLatencies;
247
248 {
249 std::scoped_lock<std::mutex> lock(mDataAccess);
250 for (uint32_t i = 0; i < mLatencyCounts.size(); i++) {
251 int32_t avg = 0;
252 if (mLatencyCounts[0] > 0) {
253 avg = mLatencyTotals[i] / mLatencyCounts[i];
254 }
255 avgLatencies.push_back(avg);
256 }
257
258 values[0].set<VendorAtomValue::repeatedIntValue>(mMinLatencies);
259 values[1].set<VendorAtomValue::repeatedIntValue>(mMaxLatencies);
260 }
261 values[2].set<VendorAtomValue::repeatedIntValue>(avgLatencies);
262
263 return VendorAtom{
264 .reverseDomainName = "",
265 .atomId = PixelAtoms::Atom::kVibratorLatencyReported,
266 .values = std::move(values),
267 };
268 }
269
vibratorErrorAtom()270 VendorAtom StatsBase::vibratorErrorAtom() {
271 STATS_TRACE("vibratorErrorAtom()");
272 std::vector<VendorAtomValue> values(1);
273
274 {
275 std::scoped_lock<std::mutex> lock(mDataAccess);
276 values[0].set<VendorAtomValue::repeatedIntValue>(mErrorCounts);
277 }
278
279 return VendorAtom{
280 .reverseDomainName = "",
281 .atomId = PixelAtoms::Atom::kVibratorErrorsReported,
282 .values = std::move(values),
283 };
284 }
285
286 } // namespace vibrator
287 } // namespace hardware
288 } // namespace android
289 } // namespace aidl
290