• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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