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 <cinttypes>
18 #include <android/binder_manager.h>
19 #include <android-base/file.h>
20 #include <pixelstats/StatsHelper.h>
21
22 #define LOG_TAG "pixelstats-vendor"
23
24 #include <utils/Log.h>
25
26 namespace android {
27 namespace hardware {
28 namespace google {
29 namespace pixel {
30
31 using aidl::android::frameworks::stats::VendorAtom;
32 using aidl::android::frameworks::stats::VendorAtomValue;
33 using android::base::ReadFileToString;
34
35 // Proto messages are 1-indexed and VendorAtom field numbers start at 2, so
36 // store everything in the values array at the index of the field number
37 // -2.
38 const int kVendorAtomOffset = 2;
39
fileExists(const std::string & path)40 bool fileExists(const std::string &path) {
41 struct stat sb;
42
43 return stat(path.c_str(), &sb) == 0;
44 }
45
getStatsService()46 std::shared_ptr<IStats> getStatsService() {
47 const std::string instance = std::string() + IStats::descriptor + "/default";
48 static bool isStatsDeclared = false;
49 if (!isStatsDeclared) {
50 // It is good to cache the result - it would not be changed
51 isStatsDeclared = AServiceManager_isDeclared(instance.c_str());
52 if (!isStatsDeclared) {
53 ALOGE("Stats service is not registered.");
54 return nullptr;
55 }
56 }
57 return IStats::fromBinder(ndk::SpAIBinder(AServiceManager_waitForService(instance.c_str())));
58 }
59
reportVendorAtom(const std::shared_ptr<IStats> & stats_client,VendorAtom event)60 void reportVendorAtom(const std::shared_ptr<IStats> &stats_client, VendorAtom event) {
61 // consecutive Atom calls should be at least 10 milliseconds apart
62 usleep(10000);
63 if (!stats_client->reportVendorAtom(event).isOk()) {
64 ALOGE("Unable to report %d to Stats service", event.atomId);
65 return;
66 }
67 }
68
reportSpeakerImpedance(const std::shared_ptr<IStats> & stats_client,const PixelAtoms::VendorSpeakerImpedance & speakerImpedance)69 void reportSpeakerImpedance(const std::shared_ptr<IStats> &stats_client,
70 const PixelAtoms::VendorSpeakerImpedance &speakerImpedance) {
71 // Load values array
72 std::vector<VendorAtomValue> values(2);
73 VendorAtomValue tmp;
74 tmp.set<VendorAtomValue::intValue>(speakerImpedance.speaker_location());
75 values[0] = tmp;
76 tmp.set<VendorAtomValue::intValue>(speakerImpedance.impedance());
77 values[1] = tmp;
78
79 // Send vendor atom to IStats HAL
80 VendorAtom event = {.reverseDomainName = "",
81 .atomId = PixelAtoms::Atom::kVendorSpeakerImpedance,
82 .values = std::move(values)};
83 const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
84 if (!ret.isOk())
85 ALOGE("Unable to report VendorSpeakerImpedance to Stats service");
86 }
87
reportSpeakerHealthStat(const std::shared_ptr<IStats> & stats_client,const PixelAtoms::VendorSpeakerStatsReported & speakerHealthStat)88 void reportSpeakerHealthStat(const std::shared_ptr<IStats> &stats_client,
89 const PixelAtoms::VendorSpeakerStatsReported &speakerHealthStat) {
90 // Load values array
91 std::vector<VendorAtomValue> values(5);
92 VendorAtomValue tmp;
93 tmp.set<VendorAtomValue::intValue>(speakerHealthStat.speaker_location());
94 values[0] = tmp;
95 tmp.set<VendorAtomValue::intValue>(speakerHealthStat.impedance());
96 values[1] = tmp;
97 tmp.set<VendorAtomValue::intValue>(speakerHealthStat.max_temperature());
98 values[2] = tmp;
99 tmp.set<VendorAtomValue::intValue>(speakerHealthStat.excursion());
100 values[3] = tmp;
101 tmp.set<VendorAtomValue::intValue>(speakerHealthStat.heartbeat());
102 values[4] = tmp;
103
104 // Send vendor atom to IStats HAL
105 VendorAtom event = {.reverseDomainName = "",
106 .atomId = PixelAtoms::Atom::kVendorSpeakerStatsReported,
107 .values = std::move(values)};
108 const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
109 if (!ret.isOk())
110 ALOGE("Unable to report VendorSpeakerStatsReported to Stats service");
111 }
112
reportSlowIo(const std::shared_ptr<IStats> & stats_client,const PixelAtoms::VendorSlowIo & slowIo)113 void reportSlowIo(const std::shared_ptr<IStats> &stats_client,
114 const PixelAtoms::VendorSlowIo &slowIo) {
115 // Load values array
116 std::vector<VendorAtomValue> values(2);
117 VendorAtomValue tmp;
118 tmp.set<VendorAtomValue::intValue>(slowIo.operation());
119 values[0] = tmp;
120 tmp.set<VendorAtomValue::intValue>(slowIo.count());
121 values[1] = tmp;
122
123 // Send vendor atom to IStats HAL
124 VendorAtom event = {.reverseDomainName = "",
125 .atomId = PixelAtoms::Atom::kVendorSlowIo,
126 .values = std::move(values)};
127 const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
128 if (!ret.isOk())
129 ALOGE("Unable to report VendorSlowIo to Stats service");
130 }
131
reportChargeCycles(const std::shared_ptr<IStats> & stats_client,const std::vector<int32_t> & chargeCycles)132 void reportChargeCycles(const std::shared_ptr<IStats> &stats_client,
133 const std::vector<int32_t> &chargeCycles) {
134 // Load values array
135 const int32_t kChargeCyclesBucketsCount =
136 PixelAtoms::VendorChargeCycles::kCycleBucket10FieldNumber - kVendorAtomOffset + 1;
137 std::vector<VendorAtomValue> values(kChargeCyclesBucketsCount);
138 VendorAtomValue tmp;
139 for (int32_t bucketIdx = 0; bucketIdx < kChargeCyclesBucketsCount; ++bucketIdx) {
140 tmp.set<VendorAtomValue::intValue>(chargeCycles[bucketIdx]);
141 values[bucketIdx] = tmp;
142 }
143
144 // Send vendor atom to IStats HAL
145 VendorAtom event = {.reverseDomainName = "",
146 .atomId = PixelAtoms::Atom::kVendorChargeCycles,
147 .values = std::move(values)};
148 const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
149 if (!ret.isOk())
150 ALOGE("Unable to report VendorChargeCycles to Stats service");
151 }
152
reportHardwareFailed(const std::shared_ptr<IStats> & stats_client,const PixelAtoms::VendorHardwareFailed & failure)153 void reportHardwareFailed(const std::shared_ptr<IStats> &stats_client,
154 const PixelAtoms::VendorHardwareFailed &failure) {
155 // Load values array
156 std::vector<VendorAtomValue> values(3);
157 VendorAtomValue tmp;
158 tmp.set<VendorAtomValue::intValue>(failure.hardware_type());
159 values[0] = tmp;
160 tmp.set<VendorAtomValue::intValue>(failure.hardware_location());
161 values[1] = tmp;
162 tmp.set<VendorAtomValue::intValue>(failure.failure_code());
163 values[2] = tmp;
164
165 // Send vendor atom to IStats HAL
166 VendorAtom event = {.reverseDomainName = "",
167 .atomId = PixelAtoms::Atom::kVendorHardwareFailed,
168 .values = std::move(values)};
169 const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
170 if (!ret.isOk())
171 ALOGE("Unable to report VendorHardwareFailed to Stats service");
172 }
173
reportSpeechDspStat(const std::shared_ptr<IStats> & stats_client,const PixelAtoms::VendorSpeechDspStat & dsp_stats)174 void reportSpeechDspStat(const std::shared_ptr<IStats> &stats_client,
175 const PixelAtoms::VendorSpeechDspStat &dsp_stats) {
176 // Load values array
177 std::vector<VendorAtomValue> values(4);
178 VendorAtomValue tmp;
179 tmp.set<VendorAtomValue::intValue>(dsp_stats.total_uptime_millis());
180 values[0] = tmp;
181 tmp.set<VendorAtomValue::intValue>(dsp_stats.total_downtime_millis());
182 values[1] = tmp;
183 tmp.set<VendorAtomValue::intValue>(dsp_stats.total_crash_count());
184 values[2] = tmp;
185 tmp.set<VendorAtomValue::intValue>(dsp_stats.total_recover_count());
186 values[3] = tmp;
187
188 // Send vendor atom to IStats HAL
189 VendorAtom event = {.reverseDomainName = "",
190 .atomId = PixelAtoms::Atom::kVendorSpeechDspStat,
191 .values = std::move(values)};
192 const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
193 if (!ret.isOk())
194 ALOGE("Unable to report VendorSpeechDspStat to Stats service");
195 }
196
reportUsbPortOverheat(const std::shared_ptr<IStats> & stats_client,const PixelAtoms::VendorUsbPortOverheat & overheat_info)197 void reportUsbPortOverheat(const std::shared_ptr<IStats> &stats_client,
198 const PixelAtoms::VendorUsbPortOverheat &overheat_info) {
199 // Load values array
200 std::vector<VendorAtomValue> values(5);
201 VendorAtomValue tmp;
202 tmp.set<VendorAtomValue::intValue>(overheat_info.plug_temperature_deci_c());
203 values[0] = tmp;
204 tmp.set<VendorAtomValue::intValue>(overheat_info.max_temperature_deci_c());
205 values[1] = tmp;
206 tmp.set<VendorAtomValue::intValue>(overheat_info.time_to_overheat_secs());
207 values[2] = tmp;
208 tmp.set<VendorAtomValue::intValue>(overheat_info.time_to_hysteresis_secs());
209 values[3] = tmp;
210 tmp.set<VendorAtomValue::intValue>(overheat_info.time_to_inactive_secs());
211 values[4] = tmp;
212
213 // Send vendor atom to IStats HAL
214 VendorAtom event = {.reverseDomainName = "",
215 .atomId = PixelAtoms::Atom::kVendorUsbPortOverheat,
216 .values = std::move(values)};
217 const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
218 if (!ret.isOk())
219 ALOGE("Unable to report VendorUsbPortOverheat to Stats service");
220 }
221
reportUsbDataSessionEvent(const std::shared_ptr<IStats> & stats_client,const PixelAtoms::VendorUsbDataSessionEvent & usb_data_event)222 void reportUsbDataSessionEvent(const std::shared_ptr<IStats> &stats_client,
223 const PixelAtoms::VendorUsbDataSessionEvent &usb_data_event) {
224 // Load values array
225 std::vector<VendorAtomValue> values(4);
226 VendorAtomValue tmp;
227 tmp.set<VendorAtomValue::intValue>(usb_data_event.usb_role());
228 values[0] = tmp;
229 tmp.set<VendorAtomValue::repeatedIntValue>(std::vector<int32_t>(
230 usb_data_event.usb_states().begin(), usb_data_event.usb_states().end()));
231 values[1] = tmp;
232 tmp.set<VendorAtomValue::repeatedLongValue>(std::vector<int64_t>(
233 usb_data_event.elapsed_time_ms().begin(), usb_data_event.elapsed_time_ms().end()));
234 values[2] = tmp;
235 tmp.set<VendorAtomValue::longValue>(usb_data_event.duration_ms());
236 values[3] = tmp;
237
238 // Send vendor atom to IStats HAL
239 VendorAtom event = {.reverseDomainName = "",
240 .atomId = PixelAtoms::Atom::kVendorUsbDataSessionEvent,
241 .values = std::move(values)};
242 const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
243 if (!ret.isOk())
244 ALOGE("Unable to report VendorUsbDataSessionEvent to Stats service");
245 }
246
readLogbuffer(const std::string & buf_path,int num_fields,uint16_t code,enum ReportEventFormat format,unsigned int last_check_time,std::vector<std::vector<uint32_t>> & events)247 void readLogbuffer(const std::string &buf_path, int num_fields, uint16_t code,
248 enum ReportEventFormat format, unsigned int last_check_time,
249 std::vector<std::vector<uint32_t>> &events) {
250 std::istringstream ss;
251 std::string file_contents, line;
252 int num, field_idx, pos, read;
253 unsigned int ts, addr, val;
254 unsigned int reported = 0;
255 uint16_t type;
256 std::vector<uint32_t> vect(num_fields);
257
258 if (!ReadFileToString(buf_path, &file_contents)) {
259 ALOGE("Unable to read logbuffer path: %s - %s", buf_path.c_str(), strerror(errno));
260 return;
261 }
262
263 ss.str(file_contents);
264 while (getline(ss, line)) {
265 num = sscanf(line.c_str(), "[%u.%*u] %hx%n", &ts, &type, &pos);
266 if (num != 2 || type != code)
267 continue;
268
269 if (last_check_time != 0 && ts <= last_check_time) {
270 reported++;
271 continue;
272 }
273
274 std::fill(vect.begin(), vect.end(), 0);
275 for (field_idx = 0; field_idx < num_fields; field_idx++, pos += read) {
276 if (format == FormatAddrWithVal) {
277 num = sscanf(&line.c_str()[pos], "%x:%x%n", &addr, &val, &read);
278 if (num != 2 || (num_fields - field_idx < 2))
279 break;
280 vect[field_idx++] = addr;
281 vect[field_idx] = val;
282 } else if (format == FormatIgnoreAddr) {
283 num = sscanf(&line.c_str()[pos], "%*[^:]:%x%n", &val, &read);
284 if (num != 1)
285 break;
286 vect[field_idx] = val;
287 } else if (format == FormatOnlyVal) {
288 num = sscanf(&line.c_str()[pos], "%x%n", &val, &read);
289 if (num != 1)
290 break;
291 vect[field_idx] = val;
292 } else {
293 break;
294 }
295 }
296
297 if (field_idx == num_fields || format == FormatOnlyVal)
298 events.push_back(vect);
299 }
300 if (events.size() > 0 || reported > 0)
301 ALOGD("0x%04X: new:%zu, reported:%d", code, events.size(), reported);
302
303 return;
304 }
305
setAtomFieldValue(std::vector<VendorAtomValue> * values,int offset,int content)306 void setAtomFieldValue(std::vector<VendorAtomValue> *values, int offset, int content) {
307 std::vector<VendorAtomValue> &val = *values;
308
309 if (offset - kVendorAtomOffset < val.size())
310 val[offset - kVendorAtomOffset].set<VendorAtomValue::intValue>(content);
311 }
312
313 } // namespace pixel
314 } // namespace google
315 } // namespace hardware
316 } // namespace android
317