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 std::shared_ptr<IStats> 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 nullptr;
137 }
138
139 ALOGI("Waiting for IStats service '%s' to come up.", kStatsInstanceName.c_str());
140 std::shared_ptr<IStats> client = IStats::fromBinder(
141 ndk::SpAIBinder(AServiceManager_waitForService(kStatsInstanceName.c_str())));
142 if (!client) {
143 ALOGE("Failed to get IStats service '%s'.", kStatsInstanceName.c_str());
144 return nullptr;
145 }
146 ALOGI("IStats service online.");
147 return client;
148 }
149
runReporterThread()150 void StatsBase::runReporterThread() {
151 STATS_TRACE("runReporterThread()");
152 using clock = std::chrono::steady_clock;
153 auto nextUpload = clock::now() + UPLOAD_INTERVAL;
154 auto status = std::cv_status::no_timeout;
155
156 while (!mTerminateReporterThread) {
157 drainAtomQueue();
158 {
159 std::unique_lock<std::mutex> lock(mAtomQueueAccess);
160 if (!mAtomQueue.empty())
161 continue;
162 status = mAtomQueueUpdated.wait_until(lock, nextUpload);
163 }
164
165 if (status == std::cv_status::timeout) {
166 nextUpload = clock::now() + UPLOAD_INTERVAL;
167 uploadDiagnostics();
168 }
169 }
170 }
171
drainAtomQueue()172 void StatsBase::drainAtomQueue() {
173 STATS_TRACE("drainAtomQueue()");
174 std::vector<VendorAtom> tempQueue;
175 {
176 std::unique_lock<std::mutex> lock(mAtomQueueAccess);
177 std::swap(mAtomQueue, tempQueue);
178 }
179
180 std::shared_ptr<IStats> client = waitForStatsService();
181 if (!client) {
182 ALOGE("Failed to get IStats service. Atoms are dropped.");
183 return;
184 }
185
186 for (const VendorAtom &atom : tempQueue) {
187 reportVendorAtom(client, atom);
188 }
189 }
190
uploadPlaycountAtoms()191 void StatsBase::uploadPlaycountAtoms() {
192 STATS_TRACE("uploadPlaycountAtoms()");
193 VendorAtom playcountAtom = vibratorPlaycountAtom();
194 reportVendorAtomAsync(playcountAtom);
195 clearData(&mWaveformCounts);
196 clearData(&mDurationCounts);
197 }
198
uploadLatencyAtoms()199 void StatsBase::uploadLatencyAtoms() {
200 STATS_TRACE("uploadLatencyAtoms()");
201 VendorAtom latencyAtom = vibratorLatencyAtom();
202 reportVendorAtomAsync(latencyAtom);
203 clearData(&mMinLatencies);
204 clearData(&mMaxLatencies);
205 clearData(&mLatencyTotals);
206 clearData(&mLatencyCounts);
207 }
208
uploadErrorAtoms()209 void StatsBase::uploadErrorAtoms() {
210 STATS_TRACE("uploadErrorAtoms()");
211 VendorAtom errorAtom = vibratorErrorAtom();
212 reportVendorAtomAsync(errorAtom);
213 clearData(&mErrorCounts);
214 }
215
clearData(std::vector<int32_t> * data)216 void StatsBase::clearData(std::vector<int32_t> *data) {
217 STATS_TRACE("clearData(data)");
218 if (data) {
219 std::scoped_lock<std::mutex> lock(mDataAccess);
220 std::fill((*data).begin(), (*data).end(), 0);
221 }
222 }
223
vibratorPlaycountAtom()224 VendorAtom StatsBase::vibratorPlaycountAtom() {
225 STATS_TRACE("vibratorPlaycountAtom()");
226 std::vector<VendorAtomValue> values(2);
227
228 {
229 std::scoped_lock<std::mutex> lock(mDataAccess);
230 values[0].set<VendorAtomValue::repeatedIntValue>(mWaveformCounts);
231 values[1].set<VendorAtomValue::repeatedIntValue>(mDurationCounts);
232 }
233
234 return VendorAtom{
235 .reverseDomainName = "",
236 .atomId = PixelAtoms::Atom::kVibratorPlaycountReported,
237 .values = std::move(values),
238 };
239 }
240
vibratorLatencyAtom()241 VendorAtom StatsBase::vibratorLatencyAtom() {
242 STATS_TRACE("vibratorLatencyAtom()");
243 std::vector<VendorAtomValue> values(3);
244 std::vector<int32_t> avgLatencies;
245
246 {
247 std::scoped_lock<std::mutex> lock(mDataAccess);
248 for (uint32_t i = 0; i < mLatencyCounts.size(); i++) {
249 int32_t avg = 0;
250 if (mLatencyCounts[0] > 0) {
251 avg = mLatencyTotals[i] / mLatencyCounts[i];
252 }
253 avgLatencies.push_back(avg);
254 }
255
256 values[0].set<VendorAtomValue::repeatedIntValue>(mMinLatencies);
257 values[1].set<VendorAtomValue::repeatedIntValue>(mMaxLatencies);
258 }
259 values[2].set<VendorAtomValue::repeatedIntValue>(avgLatencies);
260
261 return VendorAtom{
262 .reverseDomainName = "",
263 .atomId = PixelAtoms::Atom::kVibratorLatencyReported,
264 .values = std::move(values),
265 };
266 }
267
vibratorErrorAtom()268 VendorAtom StatsBase::vibratorErrorAtom() {
269 STATS_TRACE("vibratorErrorAtom()");
270 std::vector<VendorAtomValue> values(1);
271
272 {
273 std::scoped_lock<std::mutex> lock(mDataAccess);
274 values[0].set<VendorAtomValue::repeatedIntValue>(mErrorCounts);
275 }
276
277 return VendorAtom{
278 .reverseDomainName = "",
279 .atomId = PixelAtoms::Atom::kVibratorErrorsReported,
280 .values = std::move(values),
281 };
282 }
283
284 } // namespace vibrator
285 } // namespace hardware
286 } // namespace android
287 } // namespace aidl
288