• 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 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