1 /*
2 * Copyright (C) 2018 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 <pixelstats/StatsHelper.h>
18 #include <pixelstats/SysfsCollector.h>
19
20 #define LOG_TAG "pixelstats-vendor"
21
22 #include <android-base/file.h>
23 #include <android-base/parseint.h>
24 #include <android-base/properties.h>
25 #include <android-base/strings.h>
26 #include <android/binder_manager.h>
27 #include <utils/Log.h>
28 #include <utils/Timers.h>
29
30 #include <mntent.h>
31 #include <sys/timerfd.h>
32 #include <sys/vfs.h>
33 #include <cinttypes>
34 #include <string>
35
36 #ifndef ARRAY_SIZE
37 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
38 #endif
39
40 namespace android {
41 namespace hardware {
42 namespace google {
43 namespace pixel {
44
45 using aidl::android::frameworks::stats::VendorAtom;
46 using aidl::android::frameworks::stats::VendorAtomValue;
47 using android::base::ReadFileToString;
48 using android::base::StartsWith;
49 using android::base::WriteStringToFile;
50 using android::hardware::google::pixel::PixelAtoms::BatteryCapacity;
51 using android::hardware::google::pixel::PixelAtoms::BlockStatsReported;
52 using android::hardware::google::pixel::PixelAtoms::BootStatsInfo;
53 using android::hardware::google::pixel::PixelAtoms::DisplayPanelErrorStats;
54 using android::hardware::google::pixel::PixelAtoms::DisplayPortErrorStats;
55 using android::hardware::google::pixel::PixelAtoms::F2fsAtomicWriteInfo;
56 using android::hardware::google::pixel::PixelAtoms::F2fsCompressionInfo;
57 using android::hardware::google::pixel::PixelAtoms::F2fsGcSegmentInfo;
58 using android::hardware::google::pixel::PixelAtoms::F2fsSmartIdleMaintEnabledStateChanged;
59 using android::hardware::google::pixel::PixelAtoms::F2fsStatsInfo;
60 using android::hardware::google::pixel::PixelAtoms::HDCPAuthTypeStats;
61 using android::hardware::google::pixel::PixelAtoms::PartitionsUsedSpaceReported;
62 using android::hardware::google::pixel::PixelAtoms::PcieLinkStatsReported;
63 using android::hardware::google::pixel::PixelAtoms::StorageUfsHealth;
64 using android::hardware::google::pixel::PixelAtoms::StorageUfsResetCount;
65 using android::hardware::google::pixel::PixelAtoms::ThermalDfsStats;
66 using android::hardware::google::pixel::PixelAtoms::VendorAudioAdaptedInfoStatsReported;
67 using android::hardware::google::pixel::PixelAtoms::VendorAudioBtMediaStatsReported;
68 using android::hardware::google::pixel::PixelAtoms::VendorAudioHardwareStatsReported;
69 using android::hardware::google::pixel::PixelAtoms::VendorAudioOffloadedEffectStatsReported;
70 using android::hardware::google::pixel::PixelAtoms::VendorAudioPcmStatsReported;
71 using android::hardware::google::pixel::PixelAtoms::VendorAudioPdmStatsReported;
72 using android::hardware::google::pixel::PixelAtoms::VendorAudioThirdPartyEffectStatsReported;
73 using android::hardware::google::pixel::PixelAtoms::VendorChargeCycles;
74 using android::hardware::google::pixel::PixelAtoms::VendorHardwareFailed;
75 using android::hardware::google::pixel::PixelAtoms::VendorLongIRQStatsReported;
76 using android::hardware::google::pixel::PixelAtoms::VendorResumeLatencyStats;
77 using android::hardware::google::pixel::PixelAtoms::VendorSlowIo;
78 using android::hardware::google::pixel::PixelAtoms::VendorSpeakerImpedance;
79 using android::hardware::google::pixel::PixelAtoms::VendorSpeakerStatsReported;
80 using android::hardware::google::pixel::PixelAtoms::VendorSpeechDspStat;
81 using android::hardware::google::pixel::PixelAtoms::VendorTempResidencyStats;
82 using android::hardware::google::pixel::PixelAtoms::ZramBdStat;
83 using android::hardware::google::pixel::PixelAtoms::ZramMmStat;
84
SysfsCollector(const struct SysfsPaths & sysfs_paths)85 SysfsCollector::SysfsCollector(const struct SysfsPaths &sysfs_paths)
86 : kSlowioReadCntPath(sysfs_paths.SlowioReadCntPath),
87 kSlowioWriteCntPath(sysfs_paths.SlowioWriteCntPath),
88 kSlowioUnmapCntPath(sysfs_paths.SlowioUnmapCntPath),
89 kSlowioSyncCntPath(sysfs_paths.SlowioSyncCntPath),
90 kCycleCountBinsPath(sysfs_paths.CycleCountBinsPath),
91 kImpedancePath(sysfs_paths.ImpedancePath),
92 kCodecPath(sysfs_paths.CodecPath),
93 kCodec1Path(sysfs_paths.Codec1Path),
94 kSpeechDspPath(sysfs_paths.SpeechDspPath),
95 kBatteryCapacityCC(sysfs_paths.BatteryCapacityCC),
96 kBatteryCapacityVFSOC(sysfs_paths.BatteryCapacityVFSOC),
97 kUFSLifetimeA(sysfs_paths.UFSLifetimeA),
98 kUFSLifetimeB(sysfs_paths.UFSLifetimeB),
99 kUFSLifetimeC(sysfs_paths.UFSLifetimeC),
100 kF2fsStatsPath(sysfs_paths.F2fsStatsPath),
101 kZramMmStatPath("/sys/block/zram0/mm_stat"),
102 kZramBdStatPath("/sys/block/zram0/bd_stat"),
103 kEEPROMPath(sysfs_paths.EEPROMPath),
104 kBrownoutCsvPath(sysfs_paths.BrownoutCsvPath),
105 kBrownoutLogPath(sysfs_paths.BrownoutLogPath),
106 kBrownoutReasonProp(sysfs_paths.BrownoutReasonProp),
107 kPowerMitigationStatsPath(sysfs_paths.MitigationPath),
108 kPowerMitigationDurationPath(sysfs_paths.MitigationDurationPath),
109 kSpeakerTemperaturePath(sysfs_paths.SpeakerTemperaturePath),
110 kSpeakerExcursionPath(sysfs_paths.SpeakerExcursionPath),
111 kSpeakerHeartbeatPath(sysfs_paths.SpeakerHeartBeatPath),
112 kUFSErrStatsPath(sysfs_paths.UFSErrStatsPath),
113 kBlockStatsLength(sysfs_paths.BlockStatsLength),
114 kAmsRatePath(sysfs_paths.AmsRatePath),
115 kThermalStatsPaths(sysfs_paths.ThermalStatsPaths),
116 kCCARatePath(sysfs_paths.CCARatePath),
117 kTempResidencyAndResetPaths(sysfs_paths.TempResidencyAndResetPaths),
118 kLongIRQMetricsPath(sysfs_paths.LongIRQMetricsPath),
119 kStormIRQMetricsPath(sysfs_paths.StormIRQMetricsPath),
120 kIRQStatsResetPath(sysfs_paths.IRQStatsResetPath),
121 kResumeLatencyMetricsPath(sysfs_paths.ResumeLatencyMetricsPath),
122 kModemPcieLinkStatsPath(sysfs_paths.ModemPcieLinkStatsPath),
123 kWifiPcieLinkStatsPath(sysfs_paths.WifiPcieLinkStatsPath),
124 kDisplayStatsPaths(sysfs_paths.DisplayStatsPaths),
125 kDisplayPortStatsPaths(sysfs_paths.DisplayPortStatsPaths),
126 kHDCPStatsPaths(sysfs_paths.HDCPStatsPaths),
127 kPDMStatePath(sysfs_paths.PDMStatePath),
128 kWavesPath(sysfs_paths.WavesPath),
129 kAdaptedInfoCountPath(sysfs_paths.AdaptedInfoCountPath),
130 kAdaptedInfoDurationPath(sysfs_paths.AdaptedInfoDurationPath),
131 kPcmLatencyPath(sysfs_paths.PcmLatencyPath),
132 kPcmCountPath(sysfs_paths.PcmCountPath),
133 kTotalCallCountPath(sysfs_paths.TotalCallCountPath),
134 kOffloadEffectsIdPath(sysfs_paths.OffloadEffectsIdPath),
135 kOffloadEffectsDurationPath(sysfs_paths.OffloadEffectsDurationPath),
136 kBluetoothAudioUsagePath(sysfs_paths.BluetoothAudioUsagePath),
137 kGMSRPath(sysfs_paths.GMSRPath),
138 kMaxfgHistoryPath("/dev/maxfg_history"),
139 kFGModelLoadingPath(sysfs_paths.FGModelLoadingPath),
140 kFGLogBufferPath(sysfs_paths.FGLogBufferPath),
141 kSpeakerVersionPath(sysfs_paths.SpeakerVersionPath) {}
142
ReadFileToInt(const std::string & path,int * val)143 bool SysfsCollector::ReadFileToInt(const std::string &path, int *val) {
144 return ReadFileToInt(path.c_str(), val);
145 }
146
ReadFileToInt(const char * const path,int * val)147 bool SysfsCollector::ReadFileToInt(const char *const path, int *val) {
148 std::string file_contents;
149
150 if (!ReadFileToString(path, &file_contents)) {
151 ALOGE("Unable to read %s - %s", path, strerror(errno));
152 return false;
153 } else if (StartsWith(file_contents, "0x")) {
154 if (sscanf(file_contents.c_str(), "0x%x", val) != 1) {
155 ALOGE("Unable to convert %s to hex - %s", path, strerror(errno));
156 return false;
157 }
158 } else if (sscanf(file_contents.c_str(), "%d", val) != 1) {
159 ALOGE("Unable to convert %s to int - %s", path, strerror(errno));
160 return false;
161 }
162 return true;
163 }
164
165 /**
166 * Read the contents of kCycleCountBinsPath and report them via IStats HAL.
167 * The contents are expected to be N buckets total, the nth of which indicates the
168 * number of times battery %-full has been increased with the n/N% full bucket.
169 */
logBatteryChargeCycles(const std::shared_ptr<IStats> & stats_client)170 void SysfsCollector::logBatteryChargeCycles(const std::shared_ptr<IStats> &stats_client) {
171 std::string file_contents;
172 int val;
173 if (kCycleCountBinsPath == nullptr || strlen(kCycleCountBinsPath) == 0) {
174 ALOGV("Battery charge cycle path not specified");
175 return;
176 }
177 if (!ReadFileToString(kCycleCountBinsPath, &file_contents)) {
178 ALOGE("Unable to read battery charge cycles %s - %s", kCycleCountBinsPath, strerror(errno));
179 return;
180 }
181
182 const int32_t kChargeCyclesBucketsCount =
183 VendorChargeCycles::kCycleBucket10FieldNumber - kVendorAtomOffset + 1;
184 std::vector<int32_t> charge_cycles;
185 std::stringstream stream(file_contents);
186 while (stream >> val) {
187 charge_cycles.push_back(val);
188 }
189 if (charge_cycles.size() > kChargeCyclesBucketsCount) {
190 ALOGW("Got excessive battery charge cycles count %" PRIu64,
191 static_cast<uint64_t>(charge_cycles.size()));
192 } else {
193 // Push 0 for buckets that do not exist.
194 for (int bucketIdx = charge_cycles.size(); bucketIdx < kChargeCyclesBucketsCount;
195 ++bucketIdx) {
196 charge_cycles.push_back(0);
197 }
198 }
199
200 std::replace(file_contents.begin(), file_contents.end(), ' ', ',');
201 reportChargeCycles(stats_client, charge_cycles);
202 }
203
204 /**
205 * Read the contents of kEEPROMPath and report them.
206 */
logBatteryEEPROM(const std::shared_ptr<IStats> & stats_client)207 void SysfsCollector::logBatteryEEPROM(const std::shared_ptr<IStats> &stats_client) {
208 if (kEEPROMPath == nullptr || strlen(kEEPROMPath) == 0) {
209 ALOGV("Battery EEPROM path not specified");
210 } else {
211 battery_EEPROM_reporter_.checkAndReport(stats_client, kEEPROMPath);
212 }
213
214 battery_EEPROM_reporter_.checkAndReportGMSR(stats_client, kGMSRPath);
215 battery_EEPROM_reporter_.checkAndReportMaxfgHistory(stats_client, kMaxfgHistoryPath);
216 battery_EEPROM_reporter_.checkAndReportFGModelLoading(stats_client, kFGModelLoadingPath);
217 battery_EEPROM_reporter_.checkAndReportFGLearning(stats_client, kFGLogBufferPath);
218 }
219
220 /**
221 * Log battery history validation
222 */
logBatteryHistoryValidation()223 void SysfsCollector::logBatteryHistoryValidation() {
224 const std::shared_ptr<IStats> stats_client = getStatsService();
225 if (!stats_client) {
226 ALOGE("Unable to get AIDL Stats service");
227 return;
228 }
229
230 battery_EEPROM_reporter_.checkAndReportValidation(stats_client, kFGLogBufferPath);
231 }
232
233 /**
234 * Log battery health stats
235 */
logBatteryHealth(const std::shared_ptr<IStats> & stats_client)236 void SysfsCollector::logBatteryHealth(const std::shared_ptr<IStats> &stats_client) {
237 battery_health_reporter_.checkAndReportStatus(stats_client);
238 }
239
240 /**
241 * Log battery time-to-full stats
242 */
logBatteryTTF(const std::shared_ptr<IStats> & stats_client)243 void SysfsCollector::logBatteryTTF(const std::shared_ptr<IStats> &stats_client) {
244 battery_time_to_full_reporter_.checkAndReportStats(stats_client);
245 }
246
247 /**
248 * Check the codec for failures over the past 24hr.
249 */
logCodecFailed(const std::shared_ptr<IStats> & stats_client)250 void SysfsCollector::logCodecFailed(const std::shared_ptr<IStats> &stats_client) {
251 std::string file_contents;
252 if (kCodecPath == nullptr || strlen(kCodecPath) == 0) {
253 ALOGV("Audio codec path not specified");
254 return;
255 }
256 if (!ReadFileToString(kCodecPath, &file_contents)) {
257 ALOGE("Unable to read codec state %s - %s", kCodecPath, strerror(errno));
258 return;
259 }
260 if (file_contents == "0") {
261 return;
262 } else {
263 VendorHardwareFailed failure;
264 failure.set_hardware_type(VendorHardwareFailed::HARDWARE_FAILED_CODEC);
265 failure.set_hardware_location(0);
266 failure.set_failure_code(VendorHardwareFailed::COMPLETE);
267 reportHardwareFailed(stats_client, failure);
268 }
269 }
270
271 /**
272 * Check the codec1 for failures over the past 24hr.
273 */
logCodec1Failed(const std::shared_ptr<IStats> & stats_client)274 void SysfsCollector::logCodec1Failed(const std::shared_ptr<IStats> &stats_client) {
275 std::string file_contents;
276 if (kCodec1Path == nullptr || strlen(kCodec1Path) == 0) {
277 ALOGV("Audio codec1 path not specified");
278 return;
279 }
280 if (!ReadFileToString(kCodec1Path, &file_contents)) {
281 ALOGE("Unable to read codec1 state %s - %s", kCodec1Path, strerror(errno));
282 return;
283 }
284 if (file_contents == "0") {
285 return;
286 } else {
287 ALOGE("%s report hardware fail", kCodec1Path);
288 VendorHardwareFailed failure;
289 failure.set_hardware_type(VendorHardwareFailed::HARDWARE_FAILED_CODEC);
290 failure.set_hardware_location(1);
291 failure.set_failure_code(VendorHardwareFailed::COMPLETE);
292 reportHardwareFailed(stats_client, failure);
293 }
294 }
295
reportSlowIoFromFile(const std::shared_ptr<IStats> & stats_client,const char * path,const VendorSlowIo::IoOperation & operation_s)296 void SysfsCollector::reportSlowIoFromFile(const std::shared_ptr<IStats> &stats_client,
297 const char *path,
298 const VendorSlowIo::IoOperation &operation_s) {
299 std::string file_contents;
300 if (path == nullptr || strlen(path) == 0) {
301 ALOGV("slow_io path not specified");
302 return;
303 }
304 if (!ReadFileToString(path, &file_contents)) {
305 ALOGE("Unable to read slowio %s - %s", path, strerror(errno));
306 return;
307 } else {
308 int32_t slow_io_count = 0;
309 if (sscanf(file_contents.c_str(), "%d", &slow_io_count) != 1) {
310 ALOGE("Unable to parse %s from file %s to int.", file_contents.c_str(), path);
311 } else if (slow_io_count > 0) {
312 VendorSlowIo slow_io;
313 slow_io.set_operation(operation_s);
314 slow_io.set_count(slow_io_count);
315 reportSlowIo(stats_client, slow_io);
316 }
317 // Clear the stats
318 if (!android::base::WriteStringToFile("0", path, true)) {
319 ALOGE("Unable to clear SlowIO entry %s - %s", path, strerror(errno));
320 }
321 }
322 }
323
324 /**
325 * Check for slow IO operations.
326 */
logSlowIO(const std::shared_ptr<IStats> & stats_client)327 void SysfsCollector::logSlowIO(const std::shared_ptr<IStats> &stats_client) {
328 reportSlowIoFromFile(stats_client, kSlowioReadCntPath, VendorSlowIo::READ);
329 reportSlowIoFromFile(stats_client, kSlowioWriteCntPath, VendorSlowIo::WRITE);
330 reportSlowIoFromFile(stats_client, kSlowioUnmapCntPath, VendorSlowIo::UNMAP);
331 reportSlowIoFromFile(stats_client, kSlowioSyncCntPath, VendorSlowIo::SYNC);
332 }
333
334 /**
335 * Report the last-detected impedance of left & right speakers.
336 */
logSpeakerImpedance(const std::shared_ptr<IStats> & stats_client)337 void SysfsCollector::logSpeakerImpedance(const std::shared_ptr<IStats> &stats_client) {
338 std::string file_contents;
339 if (kImpedancePath == nullptr || strlen(kImpedancePath) == 0) {
340 ALOGV("Audio impedance path not specified");
341 return;
342 }
343 if (!ReadFileToString(kImpedancePath, &file_contents)) {
344 ALOGE("Unable to read impedance path %s", kImpedancePath);
345 return;
346 }
347
348 float left, right;
349 if (sscanf(file_contents.c_str(), "%g,%g", &left, &right) != 2) {
350 ALOGE("Unable to parse speaker impedance %s", file_contents.c_str());
351 return;
352 }
353 VendorSpeakerImpedance left_obj;
354 left_obj.set_speaker_location(0);
355 left_obj.set_impedance(static_cast<int32_t>(left * 1000));
356
357 VendorSpeakerImpedance right_obj;
358 right_obj.set_speaker_location(1);
359 right_obj.set_impedance(static_cast<int32_t>(right * 1000));
360
361 reportSpeakerImpedance(stats_client, left_obj);
362 reportSpeakerImpedance(stats_client, right_obj);
363 }
364
365 /**
366 * Report the last-detected impedance, temperature and heartbeats of left & right speakers.
367 */
logSpeakerHealthStats(const std::shared_ptr<IStats> & stats_client)368 void SysfsCollector::logSpeakerHealthStats(const std::shared_ptr<IStats> &stats_client) {
369 std::string file_contents_impedance;
370 std::string file_contents_temperature;
371 std::string file_contents_excursion;
372 std::string file_contents_heartbeat;
373 int count, i, version = 0;
374 float impedance_ohm[4];
375 float temperature_C[4];
376 float excursion_mm[4];
377 float heartbeat[4];
378
379 if (kImpedancePath == nullptr || strlen(kImpedancePath) == 0) {
380 ALOGD("Audio impedance path not specified");
381 return;
382 } else if (!ReadFileToString(kImpedancePath, &file_contents_impedance)) {
383 ALOGD("Unable to read speaker impedance path %s", kImpedancePath);
384 return;
385 }
386
387 if (kSpeakerTemperaturePath == nullptr || strlen(kSpeakerTemperaturePath) == 0) {
388 ALOGD("Audio speaker temperature path not specified");
389 return;
390 } else if (!ReadFileToString(kSpeakerTemperaturePath, &file_contents_temperature)) {
391 ALOGD("Unable to read speaker temperature path %s", kSpeakerTemperaturePath);
392 return;
393 }
394
395 if (kSpeakerExcursionPath == nullptr || strlen(kSpeakerExcursionPath) == 0) {
396 ALOGD("Audio speaker excursion path not specified");
397 return;
398 } else if (!ReadFileToString(kSpeakerExcursionPath, &file_contents_excursion)) {
399 ALOGD("Unable to read speaker excursion path %s", kSpeakerExcursionPath);
400 return;
401 }
402
403 if (kSpeakerHeartbeatPath == nullptr || strlen(kSpeakerHeartbeatPath) == 0) {
404 ALOGD("Audio speaker heartbeat path not specified");
405 return;
406 } else if (!ReadFileToString(kSpeakerHeartbeatPath, &file_contents_heartbeat)) {
407 ALOGD("Unable to read speaker heartbeat path %s", kSpeakerHeartbeatPath);
408 return;
409 }
410
411 if (kSpeakerVersionPath == nullptr || strlen(kSpeakerVersionPath) == 0) {
412 ALOGD("Audio speaker version path not specified. Keep version 0");
413 } else if (!ReadFileToInt(kSpeakerVersionPath, &version)) {
414 ALOGD("Unable to read version. Keep version 0");
415 }
416
417 count = sscanf(file_contents_impedance.c_str(), "%g,%g,%g,%g", &impedance_ohm[0],
418 &impedance_ohm[1], &impedance_ohm[2], &impedance_ohm[3]);
419 if (count <= 0)
420 return;
421
422 if (impedance_ohm[0] == 0 && impedance_ohm[1] == 0 && impedance_ohm[2] == 0 &&
423 impedance_ohm[3] == 0)
424 return;
425
426 count = sscanf(file_contents_temperature.c_str(), "%g,%g,%g,%g", &temperature_C[0],
427 &temperature_C[1], &temperature_C[2], &temperature_C[3]);
428 if (count <= 0)
429 return;
430
431 count = sscanf(file_contents_excursion.c_str(), "%g,%g,%g,%g", &excursion_mm[0],
432 &excursion_mm[1], &excursion_mm[2], &excursion_mm[3]);
433 if (count <= 0)
434 return;
435
436 count = sscanf(file_contents_heartbeat.c_str(), "%g,%g,%g,%g", &heartbeat[0], &heartbeat[1],
437 &heartbeat[2], &heartbeat[3]);
438 if (count <= 0)
439 return;
440
441 VendorSpeakerStatsReported obj[4];
442 for (i = 0; i < count && i < 4; i++) {
443 obj[i].set_speaker_location(i);
444 obj[i].set_impedance(static_cast<int32_t>(impedance_ohm[i] * 1000));
445 obj[i].set_max_temperature(static_cast<int32_t>(temperature_C[i] * 1000));
446 obj[i].set_excursion(static_cast<int32_t>(excursion_mm[i] * 1000));
447 obj[i].set_heartbeat(static_cast<int32_t>(heartbeat[i]));
448 obj[i].set_version(version);
449
450 reportSpeakerHealthStat(stats_client, obj[i]);
451 }
452 }
453
logDisplayStats(const std::shared_ptr<IStats> & stats_client)454 void SysfsCollector::logDisplayStats(const std::shared_ptr<IStats> &stats_client) {
455 display_stats_reporter_.logDisplayStats(stats_client, kDisplayStatsPaths,
456 DisplayStatsReporter::DISP_PANEL_STATE);
457 }
458
logDisplayPortStats(const std::shared_ptr<IStats> & stats_client)459 void SysfsCollector::logDisplayPortStats(const std::shared_ptr<IStats> &stats_client) {
460 display_stats_reporter_.logDisplayStats(stats_client, kDisplayPortStatsPaths,
461 DisplayStatsReporter::DISP_PORT_STATE);
462 }
463
logHDCPStats(const std::shared_ptr<IStats> & stats_client)464 void SysfsCollector::logHDCPStats(const std::shared_ptr<IStats> &stats_client) {
465 display_stats_reporter_.logDisplayStats(stats_client, kHDCPStatsPaths,
466 DisplayStatsReporter::HDCP_STATE);
467 }
468
logThermalStats(const std::shared_ptr<IStats> & stats_client)469 void SysfsCollector::logThermalStats(const std::shared_ptr<IStats> &stats_client) {
470 thermal_stats_reporter_.logThermalStats(stats_client, kThermalStatsPaths);
471 }
472
473 /**
474 * Report the Speech DSP state.
475 */
logSpeechDspStat(const std::shared_ptr<IStats> & stats_client)476 void SysfsCollector::logSpeechDspStat(const std::shared_ptr<IStats> &stats_client) {
477 std::string file_contents;
478 if (kSpeechDspPath == nullptr || strlen(kSpeechDspPath) == 0) {
479 ALOGV("Speech DSP path not specified");
480 return;
481 }
482 if (!ReadFileToString(kSpeechDspPath, &file_contents)) {
483 ALOGE("Unable to read speech dsp path %s", kSpeechDspPath);
484 return;
485 }
486
487 int32_t up_time = 0, down_time = 0, crash_count = 0, recover_count = 0;
488 if (sscanf(file_contents.c_str(), "%d,%d,%d,%d", &up_time, &down_time, &crash_count,
489 &recover_count) != 4) {
490 ALOGE("Unable to parse speech dsp stat %s", file_contents.c_str());
491 return;
492 }
493
494 ALOGD("SpeechDSP uptime %d downtime %d crashcount %d recovercount %d", up_time, down_time,
495 crash_count, recover_count);
496 VendorSpeechDspStat dsp_stat;
497 dsp_stat.set_total_uptime_millis(up_time);
498 dsp_stat.set_total_downtime_millis(down_time);
499 dsp_stat.set_total_crash_count(crash_count);
500 dsp_stat.set_total_recover_count(recover_count);
501
502 reportSpeechDspStat(stats_client, dsp_stat);
503 }
504
logBatteryCapacity(const std::shared_ptr<IStats> & stats_client)505 void SysfsCollector::logBatteryCapacity(const std::shared_ptr<IStats> &stats_client) {
506 std::string file_contents;
507 if (kBatteryCapacityCC == nullptr || strlen(kBatteryCapacityCC) == 0) {
508 ALOGV("Battery Capacity CC path not specified");
509 return;
510 }
511 if (kBatteryCapacityVFSOC == nullptr || strlen(kBatteryCapacityVFSOC) == 0) {
512 ALOGV("Battery Capacity VFSOC path not specified");
513 return;
514 }
515 int delta_cc_sum, delta_vfsoc_sum;
516 if (!ReadFileToInt(kBatteryCapacityCC, &delta_cc_sum) ||
517 !ReadFileToInt(kBatteryCapacityVFSOC, &delta_vfsoc_sum))
518 return;
519
520 // Load values array
521 std::vector<VendorAtomValue> values(2);
522 VendorAtomValue tmp;
523 tmp.set<VendorAtomValue::intValue>(delta_cc_sum);
524 values[BatteryCapacity::kDeltaCcSumFieldNumber - kVendorAtomOffset] = tmp;
525 tmp.set<VendorAtomValue::intValue>(delta_vfsoc_sum);
526 values[BatteryCapacity::kDeltaVfsocSumFieldNumber - kVendorAtomOffset] = tmp;
527
528 // Send vendor atom to IStats HAL
529 VendorAtom event = {.reverseDomainName = "",
530 .atomId = PixelAtoms::Atom::kBatteryCapacity,
531 .values = std::move(values)};
532 const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
533 if (!ret.isOk())
534 ALOGE("Unable to report ChargeStats to Stats service");
535 }
536
logUFSLifetime(const std::shared_ptr<IStats> & stats_client)537 void SysfsCollector::logUFSLifetime(const std::shared_ptr<IStats> &stats_client) {
538 std::string file_contents;
539 if (kUFSLifetimeA == nullptr || strlen(kUFSLifetimeA) == 0) {
540 ALOGV("UFS lifetimeA path not specified");
541 return;
542 }
543 if (kUFSLifetimeB == nullptr || strlen(kUFSLifetimeB) == 0) {
544 ALOGV("UFS lifetimeB path not specified");
545 return;
546 }
547 if (kUFSLifetimeC == nullptr || strlen(kUFSLifetimeC) == 0) {
548 ALOGV("UFS lifetimeC path not specified");
549 return;
550 }
551
552 int lifetimeA = 0, lifetimeB = 0, lifetimeC = 0;
553 if (!ReadFileToInt(kUFSLifetimeA, &lifetimeA) ||
554 !ReadFileToInt(kUFSLifetimeB, &lifetimeB) ||
555 !ReadFileToInt(kUFSLifetimeC, &lifetimeC)) {
556 ALOGE("Unable to read UFS lifetime : %s", strerror(errno));
557 return;
558 }
559
560 // Load values array
561 std::vector<VendorAtomValue> values(3);
562 VendorAtomValue tmp;
563 tmp.set<VendorAtomValue::intValue>(lifetimeA);
564 values[StorageUfsHealth::kLifetimeAFieldNumber - kVendorAtomOffset] = tmp;
565 tmp.set<VendorAtomValue::intValue>(lifetimeB);
566 values[StorageUfsHealth::kLifetimeBFieldNumber - kVendorAtomOffset] = tmp;
567 tmp.set<VendorAtomValue::intValue>(lifetimeC);
568 values[StorageUfsHealth::kLifetimeCFieldNumber - kVendorAtomOffset] = tmp;
569
570 // Send vendor atom to IStats HAL
571 VendorAtom event = {.reverseDomainName = "",
572 .atomId = PixelAtoms::Atom::kStorageUfsHealth,
573 .values = std::move(values)};
574 const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
575 if (!ret.isOk()) {
576 ALOGE("Unable to report UfsHealthStat to Stats service");
577 }
578 }
579
logUFSErrorStats(const std::shared_ptr<IStats> & stats_client)580 void SysfsCollector::logUFSErrorStats(const std::shared_ptr<IStats> &stats_client) {
581 int value, host_reset_count = 0;
582
583 if (kUFSErrStatsPath.empty() || strlen(kUFSErrStatsPath.front().c_str()) == 0) {
584 ALOGV("UFS host reset count path not specified");
585 return;
586 }
587
588 for (int i = 0; i < kUFSErrStatsPath.size(); i++) {
589 if (!ReadFileToInt(kUFSErrStatsPath[i], &value)) {
590 ALOGE("Unable to read host reset count");
591 return;
592 }
593 host_reset_count += value;
594 }
595
596 // Load values array
597 std::vector<VendorAtomValue> values(1);
598 VendorAtomValue tmp;
599 tmp.set<VendorAtomValue::intValue>(host_reset_count);
600 values[StorageUfsResetCount::kHostResetCountFieldNumber - kVendorAtomOffset] = tmp;
601
602 // Send vendor atom to IStats HAL
603 VendorAtom event = {.reverseDomainName = "",
604 .atomId = PixelAtoms::Atom::kUfsResetCount,
605 .values = std::move(values)};
606 const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
607 if (!ret.isOk()) {
608 ALOGE("Unable to report UFS host reset count to Stats service");
609 }
610 }
611
getUserDataBlock()612 static std::string getUserDataBlock() {
613 std::unique_ptr<std::FILE, int (*)(std::FILE*)> fp(setmntent("/proc/mounts", "re"), endmntent);
614 if (fp == nullptr) {
615 ALOGE("Error opening /proc/mounts");
616 return "";
617 }
618
619 mntent* mentry;
620 while ((mentry = getmntent(fp.get())) != nullptr) {
621 if (strcmp(mentry->mnt_dir, "/data") == 0) {
622 return std::string(basename(mentry->mnt_fsname));
623 }
624 }
625 return "";
626 }
627
logF2fsStats(const std::shared_ptr<IStats> & stats_client)628 void SysfsCollector::logF2fsStats(const std::shared_ptr<IStats> &stats_client) {
629 int dirty, free, cp_calls_fg, gc_calls_fg, moved_block_fg, vblocks;
630 int cp_calls_bg, gc_calls_bg, moved_block_bg;
631
632 if (kF2fsStatsPath == nullptr) {
633 ALOGE("F2fs stats path not specified");
634 return;
635 }
636
637 const std::string userdataBlock = getUserDataBlock();
638 const std::string kF2fsStatsDir = kF2fsStatsPath + userdataBlock;
639
640 if (!ReadFileToInt(kF2fsStatsDir + "/dirty_segments", &dirty)) {
641 ALOGV("Unable to read dirty segments");
642 }
643
644 if (!ReadFileToInt(kF2fsStatsDir + "/free_segments", &free)) {
645 ALOGV("Unable to read free segments");
646 }
647
648 if (!ReadFileToInt(kF2fsStatsDir + "/cp_foreground_calls", &cp_calls_fg)) {
649 ALOGV("Unable to read cp_foreground_calls");
650 }
651
652 if (!ReadFileToInt(kF2fsStatsDir + "/cp_background_calls", &cp_calls_bg)) {
653 ALOGV("Unable to read cp_background_calls");
654 }
655
656 if (!ReadFileToInt(kF2fsStatsDir + "/gc_foreground_calls", &gc_calls_fg)) {
657 ALOGV("Unable to read gc_foreground_calls");
658 }
659
660 if (!ReadFileToInt(kF2fsStatsDir + "/gc_background_calls", &gc_calls_bg)) {
661 ALOGV("Unable to read gc_background_calls");
662 }
663
664 if (!ReadFileToInt(kF2fsStatsDir + "/moved_blocks_foreground", &moved_block_fg)) {
665 ALOGV("Unable to read moved_blocks_foreground");
666 }
667
668 if (!ReadFileToInt(kF2fsStatsDir + "/moved_blocks_background", &moved_block_bg)) {
669 ALOGV("Unable to read moved_blocks_background");
670 }
671
672 if (!ReadFileToInt(kF2fsStatsDir + "/avg_vblocks", &vblocks)) {
673 ALOGV("Unable to read avg_vblocks");
674 }
675
676 // Load values array
677 std::vector<VendorAtomValue> values(9);
678 VendorAtomValue tmp;
679 tmp.set<VendorAtomValue::intValue>(dirty);
680 values[F2fsStatsInfo::kDirtySegmentsFieldNumber - kVendorAtomOffset] = tmp;
681 tmp.set<VendorAtomValue::intValue>(free);
682 values[F2fsStatsInfo::kFreeSegmentsFieldNumber - kVendorAtomOffset] = tmp;
683 tmp.set<VendorAtomValue::intValue>(cp_calls_fg);
684 values[F2fsStatsInfo::kCpCallsFgFieldNumber - kVendorAtomOffset] = tmp;
685 tmp.set<VendorAtomValue::intValue>(cp_calls_bg);
686 values[F2fsStatsInfo::kCpCallsBgFieldNumber - kVendorAtomOffset] = tmp;
687 tmp.set<VendorAtomValue::intValue>(gc_calls_fg);
688 values[F2fsStatsInfo::kGcCallsFgFieldNumber - kVendorAtomOffset] = tmp;
689 tmp.set<VendorAtomValue::intValue>(gc_calls_bg);
690 values[F2fsStatsInfo::kGcCallsBgFieldNumber - kVendorAtomOffset] = tmp;
691 tmp.set<VendorAtomValue::intValue>(moved_block_fg);
692 values[F2fsStatsInfo::kMovedBlocksFgFieldNumber - kVendorAtomOffset] = tmp;
693 tmp.set<VendorAtomValue::intValue>(moved_block_bg);
694 values[F2fsStatsInfo::kMovedBlocksBgFieldNumber - kVendorAtomOffset] = tmp;
695 tmp.set<VendorAtomValue::intValue>(vblocks);
696 values[F2fsStatsInfo::kValidBlocksFieldNumber - kVendorAtomOffset] = tmp;
697
698 // Send vendor atom to IStats HAL
699 VendorAtom event = {.reverseDomainName = "",
700 .atomId = PixelAtoms::Atom::kF2FsStats,
701 .values = std::move(values)};
702 const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
703 if (!ret.isOk()) {
704 ALOGE("Unable to report F2fs stats to Stats service");
705 }
706 }
707
logF2fsAtomicWriteInfo(const std::shared_ptr<IStats> & stats_client)708 void SysfsCollector::logF2fsAtomicWriteInfo(const std::shared_ptr<IStats> &stats_client) {
709 int peak_atomic_write, committed_atomic_block, revoked_atomic_block;
710
711 if (kF2fsStatsPath == nullptr) {
712 ALOGV("F2fs stats path not specified");
713 return;
714 }
715
716 std::string userdataBlock = getUserDataBlock();
717
718 std::string path = kF2fsStatsPath + (userdataBlock + "/peak_atomic_write");
719 if (!ReadFileToInt(path, &peak_atomic_write)) {
720 ALOGE("Unable to read peak_atomic_write");
721 return;
722 } else {
723 if (!WriteStringToFile(std::to_string(0), path)) {
724 ALOGE("Failed to write to file %s", path.c_str());
725 return;
726 }
727 }
728
729 path = kF2fsStatsPath + (userdataBlock + "/committed_atomic_block");
730 if (!ReadFileToInt(path, &committed_atomic_block)) {
731 ALOGE("Unable to read committed_atomic_block");
732 return;
733 } else {
734 if (!WriteStringToFile(std::to_string(0), path)) {
735 ALOGE("Failed to write to file %s", path.c_str());
736 return;
737 }
738 }
739
740 path = kF2fsStatsPath + (userdataBlock + "/revoked_atomic_block");
741 if (!ReadFileToInt(path, &revoked_atomic_block)) {
742 ALOGE("Unable to read revoked_atomic_block");
743 return;
744 } else {
745 if (!WriteStringToFile(std::to_string(0), path)) {
746 ALOGE("Failed to write to file %s", path.c_str());
747 return;
748 }
749 }
750
751 // Load values array
752 std::vector<VendorAtomValue> values(3);
753 values[F2fsAtomicWriteInfo::kPeakAtomicWriteFieldNumber - kVendorAtomOffset] =
754 VendorAtomValue::make<VendorAtomValue::intValue>(peak_atomic_write);
755 values[F2fsAtomicWriteInfo::kCommittedAtomicBlockFieldNumber - kVendorAtomOffset] =
756 VendorAtomValue::make<VendorAtomValue::intValue>(committed_atomic_block);
757 values[F2fsAtomicWriteInfo::kRevokedAtomicBlockFieldNumber - kVendorAtomOffset] =
758 VendorAtomValue::make<VendorAtomValue::intValue>(revoked_atomic_block);
759
760 // Send vendor atom to IStats HAL
761 VendorAtom event = {.reverseDomainName = "",
762 .atomId = PixelAtoms::Atom::kF2FsAtomicWriteInfo,
763 .values = values};
764 const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
765 if (!ret.isOk()) {
766 ALOGE("Unable to report F2fs Atomic Write info to Stats service");
767 }
768 }
769
logF2fsCompressionInfo(const std::shared_ptr<IStats> & stats_client)770 void SysfsCollector::logF2fsCompressionInfo(const std::shared_ptr<IStats> &stats_client) {
771 int compr_written_blocks, compr_saved_blocks, compr_new_inodes;
772
773 if (kF2fsStatsPath == nullptr) {
774 ALOGV("F2fs stats path not specified");
775 return;
776 }
777
778 std::string userdataBlock = getUserDataBlock();
779
780 std::string path = kF2fsStatsPath + (userdataBlock + "/compr_written_block");
781 if (!ReadFileToInt(path, &compr_written_blocks)) {
782 ALOGE("Unable to read compression written blocks");
783 return;
784 }
785
786 path = kF2fsStatsPath + (userdataBlock + "/compr_saved_block");
787 if (!ReadFileToInt(path, &compr_saved_blocks)) {
788 ALOGE("Unable to read compression saved blocks");
789 return;
790 } else {
791 if (!WriteStringToFile(std::to_string(0), path)) {
792 ALOGE("Failed to write to file %s", path.c_str());
793 return;
794 }
795 }
796
797 path = kF2fsStatsPath + (userdataBlock + "/compr_new_inode");
798 if (!ReadFileToInt(path, &compr_new_inodes)) {
799 ALOGE("Unable to read compression new inodes");
800 return;
801 } else {
802 if (!WriteStringToFile(std::to_string(0), path)) {
803 ALOGE("Failed to write to file %s", path.c_str());
804 return;
805 }
806 }
807
808 // Load values array
809 std::vector<VendorAtomValue> values(3);
810 VendorAtomValue tmp;
811 tmp.set<VendorAtomValue::intValue>(compr_written_blocks);
812 values[F2fsCompressionInfo::kComprWrittenBlocksFieldNumber - kVendorAtomOffset] = tmp;
813 tmp.set<VendorAtomValue::intValue>(compr_saved_blocks);
814 values[F2fsCompressionInfo::kComprSavedBlocksFieldNumber - kVendorAtomOffset] = tmp;
815 tmp.set<VendorAtomValue::intValue>(compr_new_inodes);
816 values[F2fsCompressionInfo::kComprNewInodesFieldNumber - kVendorAtomOffset] = tmp;
817
818 // Send vendor atom to IStats HAL
819 VendorAtom event = {.reverseDomainName = "",
820 .atomId = PixelAtoms::Atom::kF2FsCompressionInfo,
821 .values = values};
822 const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
823 if (!ret.isOk()) {
824 ALOGE("Unable to report F2fs compression info to Stats service");
825 }
826 }
827
getReclaimedSegments(const std::string & mode)828 int SysfsCollector::getReclaimedSegments(const std::string &mode) {
829 std::string userDataStatsPath = kF2fsStatsPath + getUserDataBlock();
830 std::string gcSegmentModePath = userDataStatsPath + "/gc_segment_mode";
831 std::string gcReclaimedSegmentsPath = userDataStatsPath + "/gc_reclaimed_segments";
832 int reclaimed_segments;
833
834 if (!WriteStringToFile(mode, gcSegmentModePath)) {
835 ALOGE("Failed to change gc_segment_mode to %s", mode.c_str());
836 return -1;
837 }
838
839 if (!ReadFileToInt(gcReclaimedSegmentsPath, &reclaimed_segments)) {
840 ALOGE("GC mode(%s): Unable to read gc_reclaimed_segments", mode.c_str());
841 return -1;
842 }
843
844 if (!WriteStringToFile(std::to_string(0), gcReclaimedSegmentsPath)) {
845 ALOGE("GC mode(%s): Failed to reset gc_reclaimed_segments", mode.c_str());
846 return -1;
847 }
848
849 return reclaimed_segments;
850 }
851
logF2fsGcSegmentInfo(const std::shared_ptr<IStats> & stats_client)852 void SysfsCollector::logF2fsGcSegmentInfo(const std::shared_ptr<IStats> &stats_client) {
853 int reclaimed_segments_normal, reclaimed_segments_urgent_high;
854 int reclaimed_segments_urgent_mid, reclaimed_segments_urgent_low;
855 std::string gc_normal_mode = std::to_string(0); // GC normal mode
856 std::string gc_urgent_high_mode = std::to_string(4); // GC urgent high mode
857 std::string gc_urgent_low_mode = std::to_string(5); // GC urgent low mode
858 std::string gc_urgent_mid_mode = std::to_string(6); // GC urgent mid mode
859
860 if (kF2fsStatsPath == nullptr) {
861 ALOGV("F2fs stats path not specified");
862 return;
863 }
864
865 reclaimed_segments_normal = getReclaimedSegments(gc_normal_mode);
866 if (reclaimed_segments_normal == -1) return;
867 reclaimed_segments_urgent_high = getReclaimedSegments(gc_urgent_high_mode);
868 if (reclaimed_segments_urgent_high == -1) return;
869 reclaimed_segments_urgent_low = getReclaimedSegments(gc_urgent_low_mode);
870 if (reclaimed_segments_urgent_low == -1) return;
871 reclaimed_segments_urgent_mid = getReclaimedSegments(gc_urgent_mid_mode);
872 if (reclaimed_segments_urgent_mid == -1) return;
873
874 // Load values array
875 std::vector<VendorAtomValue> values(4);
876 VendorAtomValue tmp;
877 tmp.set<VendorAtomValue::intValue>(reclaimed_segments_normal);
878 values[F2fsGcSegmentInfo::kReclaimedSegmentsNormalFieldNumber - kVendorAtomOffset] = tmp;
879 tmp.set<VendorAtomValue::intValue>(reclaimed_segments_urgent_high);
880 values[F2fsGcSegmentInfo::kReclaimedSegmentsUrgentHighFieldNumber - kVendorAtomOffset] = tmp;
881 tmp.set<VendorAtomValue::intValue>(reclaimed_segments_urgent_low);
882 values[F2fsGcSegmentInfo::kReclaimedSegmentsUrgentLowFieldNumber - kVendorAtomOffset] = tmp;
883 tmp.set<VendorAtomValue::intValue>(reclaimed_segments_urgent_mid);
884 values[F2fsGcSegmentInfo::kReclaimedSegmentsUrgentMidFieldNumber - kVendorAtomOffset] = tmp;
885
886 // Send vendor atom to IStats HAL
887 VendorAtom event = {.reverseDomainName = "",
888 .atomId = PixelAtoms::Atom::kF2FsGcSegmentInfo,
889 .values = values};
890 const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
891 if (!ret.isOk()) {
892 ALOGE("Unable to report F2fs GC Segment info to Stats service");
893 }
894 }
895
logF2fsSmartIdleMaintEnabled(const std::shared_ptr<IStats> & stats_client)896 void SysfsCollector::logF2fsSmartIdleMaintEnabled(const std::shared_ptr<IStats> &stats_client) {
897 bool smart_idle_enabled = android::base::GetBoolProperty(
898 "persist.device_config.storage_native_boot.smart_idle_maint_enabled", false);
899
900 // Load values array
901 VendorAtomValue tmp;
902 std::vector<VendorAtomValue> values(1);
903 tmp.set<VendorAtomValue::intValue>(smart_idle_enabled);
904 values[F2fsSmartIdleMaintEnabledStateChanged::kEnabledFieldNumber - kVendorAtomOffset] = tmp;
905
906 // Send vendor atom to IStats HAL
907 VendorAtom event = {.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
908 .atomId = PixelAtoms::Atom::kF2FsSmartIdleMaintEnabledStateChanged,
909 .values = std::move(values)};
910 const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
911 if (!ret.isOk()) {
912 ALOGE("Unable to report F2fsSmartIdleMaintEnabled to Stats service");
913 }
914 }
915
logBlockStatsReported(const std::shared_ptr<IStats> & stats_client)916 void SysfsCollector::logBlockStatsReported(const std::shared_ptr<IStats> &stats_client) {
917 std::string sdaPath = "/sys/block/sda/stat";
918 std::string file_contents;
919 std::string stat;
920 std::vector<std::string> stats;
921 std::stringstream ss;
922
923 // These index comes from kernel Document
924 // Documentation/ABI/stable/sysfs-block
925 const int READ_IO_IDX = 0, READ_SEC_IDX = 2, READ_TICK_IDX = 3;
926 const int WRITE_IO_IDX = 4, WRITE_SEC_IDX = 6, WRITE_TICK_IDX = 7;
927 uint64_t read_io, read_sectors, read_ticks;
928 uint64_t write_io, write_sectors, write_ticks;
929
930 if (!ReadFileToString(sdaPath.c_str(), &file_contents)) {
931 ALOGE("Failed to read block layer stat %s", sdaPath.c_str());
932 return;
933 }
934
935 ss.str(file_contents);
936 while (ss >> stat) {
937 stats.push_back(stat);
938 }
939
940 if (stats.size() < kBlockStatsLength) {
941 ALOGE("block layer stat format is incorrect %s, length %zu/%d", file_contents.c_str(),
942 stats.size(), kBlockStatsLength);
943 return;
944 }
945
946 read_io = std::stoul(stats[READ_IO_IDX]);
947 read_sectors = std::stoul(stats[READ_SEC_IDX]);
948 read_ticks = std::stoul(stats[READ_TICK_IDX]);
949 write_io = std::stoul(stats[WRITE_IO_IDX]);
950 write_sectors = std::stoul(stats[WRITE_SEC_IDX]);
951 write_ticks = std::stoul(stats[WRITE_TICK_IDX]);
952
953 // Load values array
954 std::vector<VendorAtomValue> values(6);
955 values[BlockStatsReported::kReadIoFieldNumber - kVendorAtomOffset] =
956 VendorAtomValue::make<VendorAtomValue::longValue>(read_io);
957 values[BlockStatsReported::kReadSectorsFieldNumber - kVendorAtomOffset] =
958 VendorAtomValue::make<VendorAtomValue::longValue>(read_sectors);
959 values[BlockStatsReported::kReadTicksFieldNumber - kVendorAtomOffset] =
960 VendorAtomValue::make<VendorAtomValue::longValue>(read_ticks);
961 values[BlockStatsReported::kWriteIoFieldNumber - kVendorAtomOffset] =
962 VendorAtomValue::make<VendorAtomValue::longValue>(write_io);
963 values[BlockStatsReported::kWriteSectorsFieldNumber - kVendorAtomOffset] =
964 VendorAtomValue::make<VendorAtomValue::longValue>(write_sectors);
965 values[BlockStatsReported::kWriteTicksFieldNumber - kVendorAtomOffset] =
966 VendorAtomValue::make<VendorAtomValue::longValue>(write_ticks);
967
968 // Send vendor atom to IStats HAL
969 VendorAtom event = {.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
970 .atomId = PixelAtoms::Atom::kBlockStatsReported,
971 .values = std::move(values)};
972 const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
973 if (!ret.isOk()) {
974 ALOGE("Unable to report block layer stats to Stats service");
975 }
976 }
977
logTempResidencyStats(const std::shared_ptr<IStats> & stats_client)978 void SysfsCollector::logTempResidencyStats(const std::shared_ptr<IStats> &stats_client) {
979 for (const auto &temp_residency_and_reset_path : kTempResidencyAndResetPaths) {
980 temp_residency_reporter_.logTempResidencyStats(stats_client,
981 temp_residency_and_reset_path.first,
982 temp_residency_and_reset_path.second);
983 }
984 }
985
reportZramMmStat(const std::shared_ptr<IStats> & stats_client)986 void SysfsCollector::reportZramMmStat(const std::shared_ptr<IStats> &stats_client) {
987 std::string file_contents;
988 if (!kZramMmStatPath) {
989 ALOGV("ZramMmStat path not specified");
990 return;
991 }
992
993 if (!ReadFileToString(kZramMmStatPath, &file_contents)) {
994 ALOGE("Unable to ZramMmStat %s - %s", kZramMmStatPath, strerror(errno));
995 return;
996 } else {
997 int64_t orig_data_size = 0;
998 int64_t compr_data_size = 0;
999 int64_t mem_used_total = 0;
1000 int64_t mem_limit = 0;
1001 int64_t max_used_total = 0;
1002 int64_t same_pages = 0;
1003 int64_t pages_compacted = 0;
1004 int64_t huge_pages = 0;
1005 int64_t huge_pages_since_boot = 0;
1006
1007 // huge_pages_since_boot may not exist according to kernel version.
1008 // only check if the number of collected data is equal or larger then 8
1009 if (sscanf(file_contents.c_str(),
1010 "%" SCNd64 " %" SCNd64 " %" SCNd64 " %" SCNd64 " %" SCNd64 " %" SCNd64
1011 " %" SCNd64 " %" SCNd64 " %" SCNd64,
1012 &orig_data_size, &compr_data_size, &mem_used_total, &mem_limit, &max_used_total,
1013 &same_pages, &pages_compacted, &huge_pages, &huge_pages_since_boot) < 8) {
1014 ALOGE("Unable to parse ZramMmStat %s from file %s to int.",
1015 file_contents.c_str(), kZramMmStatPath);
1016 }
1017
1018 // Load values array.
1019 // The size should be the same as the number of fields in ZramMmStat
1020 std::vector<VendorAtomValue> values(6);
1021 VendorAtomValue tmp;
1022 tmp.set<VendorAtomValue::longValue>(orig_data_size);
1023 values[ZramMmStat::kOrigDataSizeFieldNumber - kVendorAtomOffset] = tmp;
1024 tmp.set<VendorAtomValue::longValue>(compr_data_size);
1025 values[ZramMmStat::kComprDataSizeFieldNumber - kVendorAtomOffset] = tmp;
1026 tmp.set<VendorAtomValue::longValue>(mem_used_total);
1027 values[ZramMmStat::kMemUsedTotalFieldNumber - kVendorAtomOffset] = tmp;
1028 tmp.set<VendorAtomValue::longValue>(same_pages);
1029 values[ZramMmStat::kSamePagesFieldNumber - kVendorAtomOffset] = tmp;
1030 tmp.set<VendorAtomValue::longValue>(huge_pages);
1031 values[ZramMmStat::kHugePagesFieldNumber - kVendorAtomOffset] = tmp;
1032
1033 // Skip the first data to avoid a big spike in this accumulated value.
1034 if (prev_huge_pages_since_boot_ == -1)
1035 tmp.set<VendorAtomValue::longValue>(0);
1036 else
1037 tmp.set<VendorAtomValue::longValue>(huge_pages_since_boot -
1038 prev_huge_pages_since_boot_);
1039
1040 values[ZramMmStat::kHugePagesSinceBootFieldNumber - kVendorAtomOffset] = tmp;
1041 prev_huge_pages_since_boot_ = huge_pages_since_boot;
1042
1043 // Send vendor atom to IStats HAL
1044 VendorAtom event = {.reverseDomainName = "",
1045 .atomId = PixelAtoms::Atom::kZramMmStat,
1046 .values = std::move(values)};
1047 const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
1048 if (!ret.isOk())
1049 ALOGE("Zram Unable to report ZramMmStat to Stats service");
1050 }
1051 }
1052
reportZramBdStat(const std::shared_ptr<IStats> & stats_client)1053 void SysfsCollector::reportZramBdStat(const std::shared_ptr<IStats> &stats_client) {
1054 std::string file_contents;
1055 if (!kZramBdStatPath) {
1056 ALOGV("ZramBdStat path not specified");
1057 return;
1058 }
1059
1060 if (!ReadFileToString(kZramBdStatPath, &file_contents)) {
1061 ALOGE("Unable to ZramBdStat %s - %s", kZramBdStatPath, strerror(errno));
1062 return;
1063 } else {
1064 int64_t bd_count = 0;
1065 int64_t bd_reads = 0;
1066 int64_t bd_writes = 0;
1067
1068 if (sscanf(file_contents.c_str(), "%" SCNd64 " %" SCNd64 " %" SCNd64,
1069 &bd_count, &bd_reads, &bd_writes) != 3) {
1070 ALOGE("Unable to parse ZramBdStat %s from file %s to int.",
1071 file_contents.c_str(), kZramBdStatPath);
1072 }
1073
1074 // Load values array
1075 std::vector<VendorAtomValue> values(3);
1076 VendorAtomValue tmp;
1077 tmp.set<VendorAtomValue::longValue>(bd_count);
1078 values[ZramBdStat::kBdCountFieldNumber - kVendorAtomOffset] = tmp;
1079 tmp.set<VendorAtomValue::longValue>(bd_reads);
1080 values[ZramBdStat::kBdReadsFieldNumber - kVendorAtomOffset] = tmp;
1081 tmp.set<VendorAtomValue::longValue>(bd_writes);
1082 values[ZramBdStat::kBdWritesFieldNumber - kVendorAtomOffset] = tmp;
1083
1084 // Send vendor atom to IStats HAL
1085 VendorAtom event = {.reverseDomainName = "",
1086 .atomId = PixelAtoms::Atom::kZramBdStat,
1087 .values = std::move(values)};
1088 const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
1089 if (!ret.isOk())
1090 ALOGE("Zram Unable to report ZramBdStat to Stats service");
1091 }
1092 }
1093
logZramStats(const std::shared_ptr<IStats> & stats_client)1094 void SysfsCollector::logZramStats(const std::shared_ptr<IStats> &stats_client) {
1095 reportZramMmStat(stats_client);
1096 reportZramBdStat(stats_client);
1097 }
1098
logBootStats(const std::shared_ptr<IStats> & stats_client)1099 void SysfsCollector::logBootStats(const std::shared_ptr<IStats> &stats_client) {
1100 int mounted_time_sec = 0;
1101
1102 if (kF2fsStatsPath == nullptr) {
1103 ALOGE("F2fs stats path not specified");
1104 return;
1105 }
1106
1107 std::string userdataBlock = getUserDataBlock();
1108
1109 if (!ReadFileToInt(kF2fsStatsPath + (userdataBlock + "/mounted_time_sec"), &mounted_time_sec)) {
1110 ALOGV("Unable to read mounted_time_sec");
1111 return;
1112 }
1113
1114 int fsck_time_ms = android::base::GetIntProperty("ro.boottime.init.fsck.data", 0);
1115 int checkpoint_time_ms = android::base::GetIntProperty("ro.boottime.init.mount.data", 0);
1116
1117 if (fsck_time_ms == 0 && checkpoint_time_ms == 0) {
1118 ALOGV("Not yet initialized");
1119 return;
1120 }
1121
1122 // Load values array
1123 std::vector<VendorAtomValue> values(3);
1124 VendorAtomValue tmp;
1125 tmp.set<VendorAtomValue::intValue>(mounted_time_sec);
1126 values[BootStatsInfo::kMountedTimeSecFieldNumber - kVendorAtomOffset] = tmp;
1127 tmp.set<VendorAtomValue::intValue>(fsck_time_ms / 1000);
1128 values[BootStatsInfo::kFsckTimeSecFieldNumber - kVendorAtomOffset] = tmp;
1129 tmp.set<VendorAtomValue::intValue>(checkpoint_time_ms / 1000);
1130 values[BootStatsInfo::kCheckpointTimeSecFieldNumber - kVendorAtomOffset] = tmp;
1131
1132 // Send vendor atom to IStats HAL
1133 VendorAtom event = {.reverseDomainName = "",
1134 .atomId = PixelAtoms::Atom::kBootStats,
1135 .values = std::move(values)};
1136 const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
1137 if (!ret.isOk()) {
1138 ALOGE("Unable to report Boot stats to Stats service");
1139 } else {
1140 log_once_reported = true;
1141 }
1142 }
1143
1144 /**
1145 * Report the AMS & CCA rate.
1146 */
logVendorAudioHardwareStats(const std::shared_ptr<IStats> & stats_client)1147 void SysfsCollector::logVendorAudioHardwareStats(const std::shared_ptr<IStats> &stats_client) {
1148 std::string file_contents;
1149 uint32_t milli_ams_rate, c1, c2, c3, c4;
1150 uint32_t total_call_voice = 0, total_call_voip = 0;
1151 bool isAmsReady = false, isCCAReady = false;
1152
1153 if (kAmsRatePath == nullptr) {
1154 ALOGD("Audio AMS Rate path not specified");
1155 } else {
1156 if (!ReadFileToString(kAmsRatePath, &file_contents)) {
1157 ALOGD("Unable to read ams_rate path %s", kAmsRatePath);
1158 } else {
1159 if (sscanf(file_contents.c_str(), "%u", &milli_ams_rate) != 1) {
1160 ALOGD("Unable to parse ams_rate %s", file_contents.c_str());
1161 } else {
1162 isAmsReady = true;
1163 ALOGD("milli_ams_rate = %u", milli_ams_rate);
1164 }
1165 }
1166 }
1167
1168 if (kCCARatePath == nullptr) {
1169 ALOGD("Audio CCA Rate path not specified");
1170 } else {
1171 if (!ReadFileToString(kCCARatePath, &file_contents)) {
1172 ALOGD("Unable to read cca_rate path %s", kCCARatePath);
1173 } else {
1174 if (sscanf(file_contents.c_str(), "%u %u %u %u", &c1, &c2, &c3, &c4) != 4) {
1175 ALOGD("Unable to parse cca rates %s", file_contents.c_str());
1176 } else {
1177 isCCAReady = true;
1178 }
1179 }
1180 }
1181
1182 if (kTotalCallCountPath == nullptr) {
1183 ALOGD("Total call count path not specified");
1184 } else {
1185 if (!ReadFileToString(kTotalCallCountPath, &file_contents)) {
1186 ALOGD("Unable to read total call path %s", kTotalCallCountPath);
1187 } else {
1188 if (sscanf(file_contents.c_str(), "%u %u", &total_call_voice, &total_call_voip) != 2) {
1189 ALOGD("Unable to parse total call %s", file_contents.c_str());
1190 }
1191 }
1192 }
1193
1194 if (!(isAmsReady || isCCAReady)) {
1195 ALOGD("no ams or cca data to report");
1196 return;
1197 }
1198
1199 // Sending ams_rate, total_call, c1 and c2
1200 {
1201 std::vector<VendorAtomValue> values(7);
1202 VendorAtomValue tmp;
1203
1204 if (isAmsReady) {
1205 tmp.set<VendorAtomValue::intValue>(milli_ams_rate);
1206 values[VendorAudioHardwareStatsReported::kMilliRateOfAmsPerDayFieldNumber -
1207 kVendorAtomOffset] = tmp;
1208 }
1209
1210 tmp.set<VendorAtomValue::intValue>(1);
1211 values[VendorAudioHardwareStatsReported::kSourceFieldNumber - kVendorAtomOffset] = tmp;
1212
1213 if (isCCAReady) {
1214 tmp.set<VendorAtomValue::intValue>(c1);
1215 values[VendorAudioHardwareStatsReported::kCcaActiveCountPerDayFieldNumber -
1216 kVendorAtomOffset] = tmp;
1217
1218 tmp.set<VendorAtomValue::intValue>(c2);
1219 values[VendorAudioHardwareStatsReported::kCcaEnableCountPerDayFieldNumber -
1220 kVendorAtomOffset] = tmp;
1221 }
1222
1223 tmp.set<VendorAtomValue::intValue>(total_call_voice);
1224 values[VendorAudioHardwareStatsReported::kTotalCallCountPerDayFieldNumber -
1225 kVendorAtomOffset] = tmp;
1226
1227 // Send vendor atom to IStats HAL
1228 VendorAtom event = {.reverseDomainName = "",
1229 .atomId = PixelAtoms::Atom::kVendorAudioHardwareStatsReported,
1230 .values = std::move(values)};
1231 const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
1232 if (!ret.isOk())
1233 ALOGE("Unable to report VendorAudioHardwareStatsReported to Stats service");
1234 }
1235
1236 // Sending total_call, c3 and c4
1237 {
1238 std::vector<VendorAtomValue> values(7);
1239 VendorAtomValue tmp;
1240
1241 tmp.set<VendorAtomValue::intValue>(0);
1242 values[VendorAudioHardwareStatsReported::kSourceFieldNumber - kVendorAtomOffset] = tmp;
1243
1244 if (isCCAReady) {
1245 tmp.set<VendorAtomValue::intValue>(c3);
1246 values[VendorAudioHardwareStatsReported::kCcaActiveCountPerDayFieldNumber -
1247 kVendorAtomOffset] = tmp;
1248
1249 tmp.set<VendorAtomValue::intValue>(c4);
1250 values[VendorAudioHardwareStatsReported::kCcaEnableCountPerDayFieldNumber -
1251 kVendorAtomOffset] = tmp;
1252 }
1253
1254 tmp.set<VendorAtomValue::intValue>(total_call_voip);
1255 values[VendorAudioHardwareStatsReported::kTotalCallCountPerDayFieldNumber -
1256 kVendorAtomOffset] = tmp;
1257
1258 // Send vendor atom to IStats HAL
1259 VendorAtom event = {.reverseDomainName = "",
1260 .atomId = PixelAtoms::Atom::kVendorAudioHardwareStatsReported,
1261 .values = std::move(values)};
1262 const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
1263 if (!ret.isOk())
1264 ALOGE("Unable to report VendorAudioHardwareStatsReported to Stats service");
1265 }
1266 }
1267
1268 /**
1269 * Report PDM States which indicates microphone background noise level.
1270 * This function will report at most 4 atoms showing different background noise type.
1271 */
logVendorAudioPdmStatsReported(const std::shared_ptr<IStats> & stats_client)1272 void SysfsCollector::logVendorAudioPdmStatsReported(const std::shared_ptr<IStats> &stats_client) {
1273 std::string file_contents;
1274 std::vector<int> pdm_states;
1275
1276 if (kPDMStatePath == nullptr) {
1277 ALOGD("Audio PDM State path not specified");
1278 } else {
1279 if (!ReadFileToString(kPDMStatePath, &file_contents)) {
1280 ALOGD("Unable to read PDM State path %s", kPDMStatePath);
1281 } else {
1282 std::stringstream file_content_stream(file_contents);
1283 while (file_content_stream.good()) {
1284 std::string substr;
1285 int state;
1286 getline(file_content_stream, substr, ',');
1287 if (sscanf(substr.c_str(), "%d", &state) != 1) {
1288 ALOGD("Unable to parse PDM State %s", file_contents.c_str());
1289 } else {
1290 pdm_states.push_back(state);
1291 ALOGD("Parsed PDM State: %d", state);
1292 }
1293 }
1294 }
1295 }
1296 if (pdm_states.empty()) {
1297 ALOGD("Empty PDM State parsed.");
1298 return;
1299 }
1300
1301 if (pdm_states.size() > 4) {
1302 ALOGD("Too many values parsed.");
1303 return;
1304 }
1305
1306 for (int index = 0; index < pdm_states.size(); index++) {
1307 std::vector<VendorAtomValue> values(2);
1308 VendorAtomValue tmp;
1309
1310 if (pdm_states[index] == 0) {
1311 continue;
1312 }
1313
1314 tmp.set<VendorAtomValue::intValue>(index);
1315 values[VendorAudioPdmStatsReported::kPdmIndexFieldNumber - kVendorAtomOffset] = tmp;
1316
1317 tmp.set<VendorAtomValue::intValue>(pdm_states[index]);
1318 values[VendorAudioPdmStatsReported::kStateFieldNumber - kVendorAtomOffset] = tmp;
1319
1320 // Send vendor atom to IStats HAL
1321 VendorAtom event = {.reverseDomainName = "",
1322 .atomId = PixelAtoms::Atom::kVendorAudioPdmStatsReported,
1323 .values = std::move(values)};
1324
1325 const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
1326 if (!ret.isOk())
1327 ALOGE("Unable to report VendorAudioPdmStatsReported at index %d", index);
1328 }
1329 }
1330
1331 /**
1332 * Report Third party audio effects stats.
1333 * This function will report at most 5 atoms showing different instance stats.
1334 */
logWavesStats(const std::shared_ptr<IStats> & stats_client)1335 void SysfsCollector::logWavesStats(const std::shared_ptr<IStats> &stats_client) {
1336 std::string file_contents;
1337 std::vector<std::vector<int>> volume_duration_per_instance;
1338
1339 constexpr int num_instances = 5;
1340 constexpr int num_volume = 10;
1341
1342 if (kWavesPath == nullptr) {
1343 ALOGD("Audio Waves stats path not specified");
1344 return;
1345 }
1346
1347 if (!ReadFileToString(kWavesPath, &file_contents)) {
1348 ALOGD("Unable to read Wave stats path %s", kWavesPath);
1349 } else {
1350 std::stringstream file_content_stream(file_contents);
1351 int duration;
1352 std::vector<int> volume_duration;
1353 while (file_content_stream.good() && file_content_stream >> duration) {
1354 volume_duration.push_back(duration);
1355 if (volume_duration.size() >= num_volume) {
1356 volume_duration_per_instance.push_back(volume_duration);
1357 volume_duration.clear();
1358 }
1359 }
1360 }
1361
1362 if (volume_duration_per_instance.size() != num_instances) {
1363 ALOGE("Number of instances %zu doesn't match the correct number %d",
1364 volume_duration_per_instance.size(), num_instances);
1365 return;
1366 }
1367 for (int i = 0; i < volume_duration_per_instance.size(); i++) {
1368 if (volume_duration_per_instance[i].size() != num_volume) {
1369 ALOGE("Number of volume %zu doesn't match the correct number %d",
1370 volume_duration_per_instance[i].size(), num_volume);
1371 return;
1372 }
1373 }
1374
1375 std::vector<int> volume_range_field_numbers = {
1376 VendorAudioThirdPartyEffectStatsReported::kVolumeRange0ActiveMsPerDayFieldNumber,
1377 VendorAudioThirdPartyEffectStatsReported::kVolumeRange1ActiveMsPerDayFieldNumber,
1378 VendorAudioThirdPartyEffectStatsReported::kVolumeRange2ActiveMsPerDayFieldNumber,
1379 VendorAudioThirdPartyEffectStatsReported::kVolumeRange3ActiveMsPerDayFieldNumber,
1380 VendorAudioThirdPartyEffectStatsReported::kVolumeRange4ActiveMsPerDayFieldNumber,
1381 VendorAudioThirdPartyEffectStatsReported::kVolumeRange5ActiveMsPerDayFieldNumber,
1382 VendorAudioThirdPartyEffectStatsReported::kVolumeRange6ActiveMsPerDayFieldNumber,
1383 VendorAudioThirdPartyEffectStatsReported::kVolumeRange7ActiveMsPerDayFieldNumber,
1384 VendorAudioThirdPartyEffectStatsReported::kVolumeRange8ActiveMsPerDayFieldNumber,
1385 VendorAudioThirdPartyEffectStatsReported::kVolumeRange9ActiveMsPerDayFieldNumber};
1386
1387 for (int index = 0; index < volume_duration_per_instance.size(); index++) {
1388 std::vector<VendorAtomValue> values(11);
1389 VendorAtomValue tmp;
1390
1391 bool has_value = false;
1392 for (int volume_index = 0; volume_index < num_volume; volume_index++) {
1393 if (volume_duration_per_instance[index][volume_index] > 0) {
1394 has_value = true;
1395 }
1396 }
1397 if (!has_value) {
1398 continue;
1399 }
1400
1401 tmp.set<VendorAtomValue::intValue>(index);
1402 values[VendorAudioThirdPartyEffectStatsReported::kInstanceFieldNumber - kVendorAtomOffset] =
1403 tmp;
1404
1405 for (int volume_index = 0; volume_index < num_volume; volume_index++) {
1406 tmp.set<VendorAtomValue::intValue>(volume_duration_per_instance[index][volume_index]);
1407 values[volume_range_field_numbers[volume_index] - kVendorAtomOffset] = tmp;
1408 }
1409 // Send vendor atom to IStats HAL
1410 VendorAtom event = {.reverseDomainName = "",
1411 .atomId = PixelAtoms::Atom::kVendorAudioThirdPartyEffectStatsReported,
1412 .values = std::move(values)};
1413
1414 const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
1415 if (!ret.isOk())
1416 ALOGE("Unable to report VendorAudioThirdPartyEffectStatsReported at index %d", index);
1417 }
1418 }
1419
1420 /**
1421 * Report Audio Adapted Information stats such as thermal throttling.
1422 * This function will report at most 6 atoms showing different instance stats.
1423 */
logAdaptedInfoStats(const std::shared_ptr<IStats> & stats_client)1424 void SysfsCollector::logAdaptedInfoStats(const std::shared_ptr<IStats> &stats_client) {
1425 std::string file_contents;
1426 std::vector<int> count_per_feature;
1427 std::vector<int> duration_per_feature;
1428
1429 constexpr int num_features = 6;
1430
1431 if (kAdaptedInfoCountPath == nullptr) {
1432 ALOGD("Audio Adapted Info Count stats path not specified");
1433 return;
1434 }
1435
1436 if (kAdaptedInfoDurationPath == nullptr) {
1437 ALOGD("Audio Adapted Info Duration stats path not specified");
1438 return;
1439 }
1440
1441 if (!ReadFileToString(kAdaptedInfoCountPath, &file_contents)) {
1442 ALOGD("Unable to read Adapted Info Count stats path %s", kAdaptedInfoCountPath);
1443 } else {
1444 std::stringstream file_content_stream(file_contents);
1445 int count;
1446 while (file_content_stream.good() && file_content_stream >> count) {
1447 count_per_feature.push_back(count);
1448 }
1449 }
1450
1451 if (count_per_feature.size() != num_features) {
1452 ALOGD("Audio Adapted Info Count doesn't match the number of features. %zu / %d",
1453 count_per_feature.size(), num_features);
1454 return;
1455 }
1456
1457 if (!ReadFileToString(kAdaptedInfoDurationPath, &file_contents)) {
1458 ALOGD("Unable to read Adapted Info Duration stats path %s", kAdaptedInfoDurationPath);
1459 } else {
1460 std::stringstream file_content_stream(file_contents);
1461 int duration;
1462 while (file_content_stream.good() && file_content_stream >> duration) {
1463 duration_per_feature.push_back(duration);
1464 }
1465 }
1466
1467 if (duration_per_feature.size() != num_features) {
1468 ALOGD("Audio Adapted Info Duration doesn't match the number of features. %zu / %d",
1469 duration_per_feature.size(), num_features);
1470 return;
1471 }
1472
1473 for (int index = 0; index < num_features; index++) {
1474 std::vector<VendorAtomValue> values(3);
1475 VendorAtomValue tmp;
1476
1477 if (count_per_feature[index] == 0 && duration_per_feature[index] == 0) {
1478 continue;
1479 }
1480
1481 tmp.set<VendorAtomValue::intValue>(index);
1482 values[VendorAudioAdaptedInfoStatsReported::kFeatureIdFieldNumber - kVendorAtomOffset] =
1483 tmp;
1484
1485 tmp.set<VendorAtomValue::intValue>(count_per_feature[index]);
1486 values[VendorAudioAdaptedInfoStatsReported::kActiveCountsPerDayFieldNumber -
1487 kVendorAtomOffset] = tmp;
1488
1489 tmp.set<VendorAtomValue::intValue>(duration_per_feature[index]);
1490 values[VendorAudioAdaptedInfoStatsReported::kActiveDurationMsPerDayFieldNumber -
1491 kVendorAtomOffset] = tmp;
1492
1493 // Send vendor atom to IStats HAL
1494 VendorAtom event = {.reverseDomainName = "",
1495 .atomId = PixelAtoms::Atom::kVendorAudioAdaptedInfoStatsReported,
1496 .values = std::move(values)};
1497
1498 const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
1499 if (!ret.isOk())
1500 ALOGE("Unable to report VendorAudioAdaptedInfoStatsReported at index %d", index);
1501 }
1502 }
1503
1504 /**
1505 * Report audio PCM usage stats such as latency and active count.
1506 * This function will report at most 19 atoms showing different PCM type.
1507 */
logPcmUsageStats(const std::shared_ptr<IStats> & stats_client)1508 void SysfsCollector::logPcmUsageStats(const std::shared_ptr<IStats> &stats_client) {
1509 std::string file_contents;
1510 std::vector<int> count_per_type;
1511 std::vector<int> latency_per_type;
1512
1513 constexpr int num_type = 19;
1514
1515 if (kPcmLatencyPath == nullptr) {
1516 ALOGD("PCM Latency path not specified");
1517 return;
1518 }
1519
1520 if (kPcmCountPath == nullptr) {
1521 ALOGD("PCM Count path not specified");
1522 return;
1523 }
1524
1525 if (!ReadFileToString(kPcmCountPath, &file_contents)) {
1526 ALOGD("Unable to read PCM Count path %s", kPcmCountPath);
1527 } else {
1528 std::stringstream file_content_stream(file_contents);
1529 int count;
1530 while (file_content_stream.good() && file_content_stream >> count) {
1531 count_per_type.push_back(count);
1532 }
1533 }
1534
1535 if (count_per_type.size() != num_type) {
1536 ALOGD("Audio PCM Count path doesn't match the number of features. %zu / %d",
1537 count_per_type.size(), num_type);
1538 return;
1539 }
1540
1541 if (!ReadFileToString(kPcmLatencyPath, &file_contents)) {
1542 ALOGD("Unable to read PCM Latency path %s", kPcmLatencyPath);
1543 } else {
1544 std::stringstream file_content_stream(file_contents);
1545 int duration;
1546 while (file_content_stream.good() && file_content_stream >> duration) {
1547 latency_per_type.push_back(duration);
1548 }
1549 }
1550
1551 if (latency_per_type.size() != num_type) {
1552 ALOGD("Audio PCM Latency path doesn't match the number of features. %zu / %d",
1553 latency_per_type.size(), num_type);
1554 return;
1555 }
1556
1557 for (int index = 0; index < num_type; index++) {
1558 std::vector<VendorAtomValue> values(3);
1559 VendorAtomValue tmp;
1560
1561 if (latency_per_type[index] == 0 && count_per_type[index] == 0) {
1562 continue;
1563 }
1564
1565 tmp.set<VendorAtomValue::intValue>(index);
1566 values[VendorAudioPcmStatsReported::kTypeFieldNumber - kVendorAtomOffset] = tmp;
1567
1568 tmp.set<VendorAtomValue::intValue>(latency_per_type[index]);
1569 values[VendorAudioPcmStatsReported::kPcmOpenLatencyAvgMsPerDayFieldNumber -
1570 kVendorAtomOffset] = tmp;
1571
1572 tmp.set<VendorAtomValue::intValue>(count_per_type[index]);
1573 values[VendorAudioPcmStatsReported::kPcmActiveCountsPerDayFieldNumber - kVendorAtomOffset] =
1574 tmp;
1575
1576 // Send vendor atom to IStats HAL
1577 VendorAtom event = {.reverseDomainName = "",
1578 .atomId = PixelAtoms::Atom::kVendorAudioPcmStatsReported,
1579 .values = std::move(values)};
1580
1581 const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
1582 if (!ret.isOk())
1583 ALOGE("Unable to report VendorAudioPcmStatsReported at index %d", index);
1584 }
1585 }
1586
1587 /**
1588 * Report audio Offload Effects usage stats duration per day.
1589 */
logOffloadEffectsStats(const std::shared_ptr<IStats> & stats_client)1590 void SysfsCollector::logOffloadEffectsStats(const std::shared_ptr<IStats> &stats_client) {
1591 std::string file_contents;
1592 std::vector<int> uuids;
1593 std::vector<int> durations;
1594
1595 if (kOffloadEffectsIdPath == nullptr) {
1596 ALOGD("Offload Effects ID Path is not specified");
1597 return;
1598 }
1599
1600 if (kOffloadEffectsDurationPath == nullptr) {
1601 ALOGD("Offload Effects Duration Path is not specified");
1602 return;
1603 }
1604
1605 if (!ReadFileToString(kOffloadEffectsIdPath, &file_contents)) {
1606 ALOGD("Unable to read Offload Effect ID path %s", kOffloadEffectsIdPath);
1607 } else {
1608 std::stringstream file_content_stream(file_contents);
1609 int uuid;
1610 while (file_content_stream.good() && file_content_stream >> uuid) {
1611 uuids.push_back(uuid);
1612 }
1613 }
1614
1615 if (!ReadFileToString(kOffloadEffectsDurationPath, &file_contents)) {
1616 ALOGD("Unable to read Offload Effect duration path %s", kOffloadEffectsDurationPath);
1617 } else {
1618 std::stringstream file_content_stream(file_contents);
1619 int duration;
1620 while (file_content_stream.good() && file_content_stream >> duration) {
1621 durations.push_back(duration);
1622 }
1623 }
1624
1625 if (durations.size() * 4 != uuids.size()) {
1626 ALOGD("ID and duration data does not match: %zu and %zu", durations.size(), uuids.size());
1627 return;
1628 }
1629
1630 for (int index = 0; index < durations.size(); index++) {
1631 std::vector<VendorAtomValue> values(3);
1632 VendorAtomValue tmp;
1633 int64_t uuid_msb = ((int64_t)uuids[index * 4] << 32 | uuids[index * 4 + 1]);
1634 int64_t uuid_lsb = ((int64_t)uuids[index * 4 + 2] << 32 | uuids[index * 4 + 3]);
1635
1636 if (uuid_msb == 0 && uuid_lsb == 0) {
1637 continue;
1638 }
1639
1640 tmp.set<VendorAtomValue::VendorAtomValue::longValue>(uuid_msb);
1641 values[VendorAudioOffloadedEffectStatsReported::kEffectUuidMsbFieldNumber -
1642 kVendorAtomOffset] = tmp;
1643
1644 tmp.set<VendorAtomValue::VendorAtomValue::longValue>(uuid_lsb);
1645 values[VendorAudioOffloadedEffectStatsReported::kEffectUuidLsbFieldNumber -
1646 kVendorAtomOffset] = tmp;
1647
1648 tmp.set<VendorAtomValue::intValue>(durations[index]);
1649 values[VendorAudioOffloadedEffectStatsReported::kEffectActiveSecondsPerDayFieldNumber -
1650 kVendorAtomOffset] = tmp;
1651
1652 // Send vendor atom to IStats HAL
1653 VendorAtom event = {.reverseDomainName = "",
1654 .atomId = PixelAtoms::Atom::kVendorAudioOffloadedEffectStatsReported,
1655 .values = std::move(values)};
1656
1657 const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
1658 if (!ret.isOk()) {
1659 ALOGE("Unable to report VendorAudioOffloadedEffectStatsReported at index %d", index);
1660 } else {
1661 ALOGD("Reported VendorAudioOffloadedEffectStatsReported successfully at index %d",
1662 index);
1663 }
1664 }
1665 }
1666
1667 /**
1668 * Report bluetooth audio usage stats.
1669 * This function will report at most 5 atoms showing different instance stats.
1670 */
logBluetoothAudioUsage(const std::shared_ptr<IStats> & stats_client)1671 void SysfsCollector::logBluetoothAudioUsage(const std::shared_ptr<IStats> &stats_client) {
1672 std::string file_contents;
1673 std::vector<int> duration_per_codec;
1674
1675 constexpr int num_codec = 5;
1676
1677 if (kBluetoothAudioUsagePath == nullptr) {
1678 ALOGD("Bluetooth Audio stats path not specified");
1679 return;
1680 }
1681
1682 if (!ReadFileToString(kBluetoothAudioUsagePath, &file_contents)) {
1683 ALOGD("Unable to read Bluetooth Audio stats path %s", kBluetoothAudioUsagePath);
1684 } else {
1685 std::stringstream file_content_stream(file_contents);
1686 int duration;
1687 while (file_content_stream.good() && file_content_stream >> duration) {
1688 duration_per_codec.push_back(duration);
1689 }
1690 }
1691
1692 if (duration_per_codec.size() != num_codec) {
1693 ALOGD("Bluetooth Audio num codec != number of codec. %zu / %d", duration_per_codec.size(),
1694 num_codec);
1695 return;
1696 }
1697
1698 for (int index = 0; index < num_codec; index++) {
1699 std::vector<VendorAtomValue> values(2);
1700 VendorAtomValue tmp;
1701
1702 if (duration_per_codec[index] == 0) {
1703 ALOGD("Skipped VendorAudioBtMediaStatsReported at codec:%d", index);
1704 continue;
1705 }
1706
1707 tmp.set<VendorAtomValue::intValue>(index);
1708 values[VendorAudioBtMediaStatsReported::kBtCodecTypeFieldNumber - kVendorAtomOffset] = tmp;
1709
1710 tmp.set<VendorAtomValue::intValue>(duration_per_codec[index]);
1711 values[VendorAudioBtMediaStatsReported::kActiveSecondsPerDayFieldNumber -
1712 kVendorAtomOffset] = tmp;
1713
1714 // Send vendor atom to IStats HAL
1715 VendorAtom event = {.reverseDomainName = "",
1716 .atomId = PixelAtoms::Atom::kVendorAudioBtMediaStatsReported,
1717 .values = std::move(values)};
1718
1719 const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
1720 if (!ret.isOk())
1721 ALOGE("Unable to report VendorAudioBtMediaStatsReported at index %d", index);
1722 else
1723 ALOGD("Reporting VendorAudioBtMediaStatsReported: codec:%d, duration:%d", index,
1724 duration_per_codec[index]);
1725 }
1726 }
1727
1728 /**
1729 * Logs the Resume Latency stats.
1730 */
logVendorResumeLatencyStats(const std::shared_ptr<IStats> & stats_client)1731 void SysfsCollector::logVendorResumeLatencyStats(const std::shared_ptr<IStats> &stats_client) {
1732 std::string uart_enabled = android::base::GetProperty("init.svc.console", "");
1733 if (uart_enabled == "running") {
1734 return;
1735 }
1736 std::string file_contents;
1737 if (!kResumeLatencyMetricsPath) {
1738 ALOGE("ResumeLatencyMetrics path not specified");
1739 return;
1740 }
1741 if (!ReadFileToString(kResumeLatencyMetricsPath, &file_contents)) {
1742 ALOGE("Unable to ResumeLatencyMetric %s - %s", kResumeLatencyMetricsPath, strerror(errno));
1743 return;
1744 }
1745
1746 int offset = 0;
1747 int bytes_read;
1748 const char *data = file_contents.c_str();
1749 int data_len = file_contents.length();
1750
1751 int curr_bucket_cnt;
1752 if (!sscanf(data + offset, "Resume Latency Bucket Count: %d\n%n", &curr_bucket_cnt,
1753 &bytes_read))
1754 return;
1755 offset += bytes_read;
1756 if (offset >= data_len)
1757 return;
1758
1759 int64_t max_latency;
1760 if (!sscanf(data + offset, "Max Resume Latency: %" PRId64 "\n%n", &max_latency, &bytes_read))
1761 return;
1762 offset += bytes_read;
1763 if (offset >= data_len)
1764 return;
1765
1766 uint64_t sum_latency;
1767 if (!sscanf(data + offset, "Sum Resume Latency: %" PRIu64 "\n%n", &sum_latency, &bytes_read))
1768 return;
1769 offset += bytes_read;
1770 if (offset >= data_len)
1771 return;
1772
1773 if (curr_bucket_cnt > kMaxResumeLatencyBuckets)
1774 return;
1775 if (curr_bucket_cnt != prev_data.bucket_cnt) {
1776 prev_data.resume_latency_buckets.clear();
1777 }
1778
1779 int64_t total_latency_cnt = 0;
1780 int64_t count;
1781 int index = 2;
1782 std::vector<VendorAtomValue> values(curr_bucket_cnt + 2);
1783 VendorAtomValue tmp;
1784 // Iterate over resume latency buckets to get latency count within some latency thresholds
1785 while (sscanf(data + offset, "%*ld - %*ldms ====> %" PRId64 "\n%n", &count, &bytes_read) == 1 ||
1786 sscanf(data + offset, "%*ld - infms ====> %" PRId64 "\n%n", &count, &bytes_read) == 1) {
1787 offset += bytes_read;
1788 if (offset >= data_len && (index + 1 < curr_bucket_cnt + 2))
1789 return;
1790 if (curr_bucket_cnt == prev_data.bucket_cnt) {
1791 tmp.set<VendorAtomValue::longValue>(count -
1792 prev_data.resume_latency_buckets[index - 2]);
1793 prev_data.resume_latency_buckets[index - 2] = count;
1794 } else {
1795 tmp.set<VendorAtomValue::longValue>(count);
1796 prev_data.resume_latency_buckets.push_back(count);
1797 }
1798 if (index >= curr_bucket_cnt + 2)
1799 return;
1800 values[index] = tmp;
1801 index += 1;
1802 total_latency_cnt += count;
1803 }
1804 tmp.set<VendorAtomValue::longValue>(max_latency);
1805 values[0] = tmp;
1806 if ((sum_latency - prev_data.resume_latency_sum_ms < 0) ||
1807 (total_latency_cnt - prev_data.resume_count <= 0)) {
1808 tmp.set<VendorAtomValue::longValue>(-1);
1809 ALOGI("average resume latency get overflow");
1810 } else {
1811 tmp.set<VendorAtomValue::longValue>(
1812 (int64_t)(sum_latency - prev_data.resume_latency_sum_ms) /
1813 (total_latency_cnt - prev_data.resume_count));
1814 }
1815 values[1] = tmp;
1816
1817 prev_data.resume_latency_sum_ms = sum_latency;
1818 prev_data.resume_count = total_latency_cnt;
1819 prev_data.bucket_cnt = curr_bucket_cnt;
1820 // Send vendor atom to IStats HAL
1821 VendorAtom event = {.reverseDomainName = "",
1822 .atomId = PixelAtoms::Atom::kVendorResumeLatencyStats,
1823 .values = std::move(values)};
1824 const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
1825 if (!ret.isOk())
1826 ALOGE("Unable to report VendorResumeLatencyStats to Stats service");
1827 }
1828
1829 /**
1830 * Read and store top 5 irq stats.
1831 */
process_irqatom_values(std::string file_contents,int * offset,std::vector<VendorAtomValue> * values)1832 void process_irqatom_values(std::string file_contents, int *offset,
1833 std::vector<VendorAtomValue> *values) {
1834 const char *data = file_contents.c_str();
1835 int bytes_read;
1836 int64_t irq_data;
1837 int irq_num;
1838
1839 std::vector<std::pair<int, int64_t>> irq_pair;
1840
1841 while (sscanf(data + *offset, "%d %" PRId64 "\n%n", &irq_num, &irq_data, &bytes_read) == 2) {
1842 irq_pair.push_back(std::make_pair(irq_num, irq_data));
1843 *offset += bytes_read;
1844 }
1845 VendorAtomValue tmp;
1846 int irq_stats_size = irq_pair.size();
1847 for (int i = 0; i < 5; i++) {
1848 if (irq_stats_size < 5 && i >= irq_stats_size) {
1849 tmp.set<VendorAtomValue::longValue>(-1);
1850 values->push_back(tmp);
1851 tmp.set<VendorAtomValue::longValue>(0);
1852 values->push_back(tmp);
1853 } else {
1854 tmp.set<VendorAtomValue::longValue>(irq_pair[i].first);
1855 values->push_back(tmp);
1856 tmp.set<VendorAtomValue::longValue>(irq_pair[i].second);
1857 values->push_back(tmp);
1858 }
1859 }
1860 }
1861
1862 /**
1863 * Logs the Long irq stats.
1864 */
logVendorLongIRQStatsReported(const std::shared_ptr<IStats> & stats_client)1865 void SysfsCollector::logVendorLongIRQStatsReported(const std::shared_ptr<IStats> &stats_client) {
1866 std::string uart_enabled = android::base::GetProperty("init.svc.console", "");
1867 if (uart_enabled == "running") {
1868 return;
1869 }
1870 std::string irq_file_contents, storm_file_contents;
1871 if (kLongIRQMetricsPath == nullptr || strlen(kLongIRQMetricsPath) == 0) {
1872 ALOGV("LongIRQ path not specified");
1873 return;
1874 }
1875 if (!ReadFileToString(kLongIRQMetricsPath, &irq_file_contents)) {
1876 ALOGE("Unable to read LongIRQ %s - %s", kLongIRQMetricsPath, strerror(errno));
1877 return;
1878 }
1879 if (kStormIRQMetricsPath == nullptr || strlen(kStormIRQMetricsPath) == 0) {
1880 ALOGV("StormIRQ path not specified");
1881 return;
1882 }
1883 if (!ReadFileToString(kStormIRQMetricsPath, &storm_file_contents)) {
1884 ALOGE("Unable to read StormIRQ %s - %s", kStormIRQMetricsPath, strerror(errno));
1885 return;
1886 }
1887 if (kIRQStatsResetPath == nullptr || strlen(kIRQStatsResetPath) == 0) {
1888 ALOGV("IRQStatsReset path not specified");
1889 return;
1890 }
1891 int offset = 0;
1892 int bytes_read;
1893 const char *data = irq_file_contents.c_str();
1894
1895 // Get, process softirq stats
1896 int64_t irq_count;
1897 if (sscanf(data + offset, "long SOFTIRQ count: %" PRId64 "\n%n", &irq_count, &bytes_read) != 1)
1898 return;
1899 offset += bytes_read;
1900 std::vector<VendorAtomValue> values;
1901 VendorAtomValue tmp;
1902 tmp.set<VendorAtomValue::longValue>(irq_count);
1903 values.push_back(tmp);
1904
1905 if (sscanf(data + offset, "long SOFTIRQ detail (num, latency):\n%n", &bytes_read) != 0)
1906 return;
1907 offset += bytes_read;
1908 process_irqatom_values(data, &offset, &values);
1909
1910 // Get, process irq stats
1911 if (sscanf(data + offset, "long IRQ count: %" PRId64 "\n%n", &irq_count, &bytes_read) != 1)
1912 return;
1913 offset += bytes_read;
1914 tmp.set<VendorAtomValue::longValue>(irq_count);
1915 values.push_back(tmp);
1916
1917 if (sscanf(data + offset, "long IRQ detail (num, latency):\n%n", &bytes_read) != 0)
1918 return;
1919 offset += bytes_read;
1920 process_irqatom_values(data, &offset, &values);
1921
1922 // Get, process storm irq stats
1923 offset = 0;
1924 data = storm_file_contents.c_str();
1925 if (sscanf(data + offset, "storm IRQ detail (num, storm_count):\n%n", &bytes_read) != 0)
1926 return;
1927 offset += bytes_read;
1928 process_irqatom_values(data, &offset, &values);
1929
1930 // Send vendor atom to IStats HAL
1931 VendorAtom event = {.reverseDomainName = "",
1932 .atomId = PixelAtoms::Atom::kVendorLongIrqStatsReported,
1933 .values = std::move(values)};
1934 const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
1935 if (!ret.isOk())
1936 ALOGE("Unable to report kVendorLongIRQStatsReported to Stats service");
1937
1938 // Reset irq stats
1939 if (!WriteStringToFile(std::to_string(1), kIRQStatsResetPath)) {
1940 ALOGE("Failed to write to stats_reset");
1941 return;
1942 }
1943 }
1944
logPartitionUsedSpace(const std::shared_ptr<IStats> & stats_client)1945 void SysfsCollector::logPartitionUsedSpace(const std::shared_ptr<IStats> &stats_client) {
1946 struct statfs fs_info;
1947 char path[] = "/mnt/vendor/persist";
1948
1949 if (statfs(path, &fs_info) == -1) {
1950 ALOGE("statfs: %s", strerror(errno));
1951 return;
1952 }
1953
1954 // Load values array
1955 std::vector<VendorAtomValue> values(3);
1956 values[PartitionsUsedSpaceReported::kDirectoryFieldNumber - kVendorAtomOffset] =
1957 VendorAtomValue::make<VendorAtomValue::intValue>
1958 (PixelAtoms::PartitionsUsedSpaceReported::PERSIST);
1959 values[PartitionsUsedSpaceReported::kFreeBytesFieldNumber - kVendorAtomOffset] =
1960 VendorAtomValue::make<VendorAtomValue::longValue>(fs_info.f_bsize * fs_info.f_bfree);
1961 values[PartitionsUsedSpaceReported::kTotalBytesFieldNumber - kVendorAtomOffset] =
1962 VendorAtomValue::make<VendorAtomValue::longValue>(fs_info.f_bsize * fs_info.f_blocks);
1963 // Send vendor atom to IStats HAL
1964 VendorAtom event = {.reverseDomainName = "",
1965 .atomId = PixelAtoms::Atom::kPartitionUsedSpaceReported,
1966 .values = std::move(values)};
1967
1968 const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
1969 if (!ret.isOk()) {
1970 ALOGE("Unable to report Partitions Used Space Reported to stats service");
1971 }
1972 }
1973
logPcieLinkStats(const std::shared_ptr<IStats> & stats_client)1974 void SysfsCollector::logPcieLinkStats(const std::shared_ptr<IStats> &stats_client) {
1975 struct sysfs_map {
1976 const char *sysfs_path;
1977 bool is_counter;
1978 int modem_val;
1979 int wifi_val;
1980 int modem_msg_field_number;
1981 int wifi_msg_field_number;
1982 };
1983
1984 int i;
1985 bool reportPcieLinkStats = false;
1986
1987 /* Map sysfs data to PcieLinkStatsReported message elements */
1988 struct sysfs_map datamap[] = {
1989 {"link_down_irqs", true, 0, 0,
1990 PcieLinkStatsReported::kModemPcieLinkdownsFieldNumber,
1991 PcieLinkStatsReported::kWifiPcieLinkdownsFieldNumber},
1992
1993 {"complete_timeout_irqs", true, 0, 0,
1994 PcieLinkStatsReported::kModemPcieCompletionTimeoutsFieldNumber,
1995 PcieLinkStatsReported::kWifiPcieCompletionTimeoutsFieldNumber},
1996
1997 {"link_up_failures", true, 0, 0,
1998 PcieLinkStatsReported::kModemPcieLinkupFailuresFieldNumber,
1999 PcieLinkStatsReported::kWifiPcieLinkupFailuresFieldNumber},
2000
2001 {"link_recovery_failures", true, 0, 0,
2002 PcieLinkStatsReported::kModemPcieLinkRecoveryFailuresFieldNumber,
2003 PcieLinkStatsReported::kWifiPcieLinkRecoveryFailuresFieldNumber},
2004
2005 {"pll_lock_average", false, 0, 0,
2006 PcieLinkStatsReported::kModemPciePllLockAvgFieldNumber,
2007 PcieLinkStatsReported::kWifiPciePllLockAvgFieldNumber},
2008
2009 {"link_up_average", false, 0, 0,
2010 PcieLinkStatsReported::kModemPcieLinkUpAvgFieldNumber,
2011 PcieLinkStatsReported::kWifiPcieLinkUpAvgFieldNumber },
2012 };
2013
2014
2015 if (kModemPcieLinkStatsPath == nullptr) {
2016 ALOGD("Modem PCIe stats path not specified");
2017 } else {
2018 for (i=0; i < ARRAY_SIZE(datamap); i++) {
2019 std::string modempath =
2020 std::string(kModemPcieLinkStatsPath) + "/" + datamap[i].sysfs_path;
2021
2022 if (ReadFileToInt(modempath, &(datamap[i].modem_val))) {
2023 reportPcieLinkStats = true;
2024 ALOGD("Modem %s = %d", datamap[i].sysfs_path,
2025 datamap[i].modem_val);
2026 if (datamap[i].is_counter) {
2027 std::string value = std::to_string(datamap[i].modem_val);
2028 /* Writing the value back clears the counter */
2029 if (!WriteStringToFile(value, modempath)) {
2030 ALOGE("Unable to clear modem PCIe statistics file: %s - %s",
2031 modempath.c_str(), strerror(errno));
2032 }
2033 }
2034 }
2035 }
2036 }
2037
2038 if (kWifiPcieLinkStatsPath == nullptr) {
2039 ALOGD("Wifi PCIe stats path not specified");
2040 } else {
2041 for (i=0; i < ARRAY_SIZE(datamap); i++) {
2042 std::string wifipath =
2043 std::string(kWifiPcieLinkStatsPath) + "/" + datamap[i].sysfs_path;
2044
2045 if (ReadFileToInt(wifipath, &(datamap[i].wifi_val))) {
2046 reportPcieLinkStats = true;
2047 ALOGD("Wifi %s = %d", datamap[i].sysfs_path,
2048 datamap[i].wifi_val);
2049 if (datamap[i].is_counter) {
2050 std::string value = std::to_string(datamap[i].wifi_val);
2051 /* Writing the value back clears the counter */
2052 if (!WriteStringToFile(value, wifipath)) {
2053 ALOGE("Unable to clear wifi PCIe statistics file: %s - %s",
2054 wifipath.c_str(), strerror(errno));
2055 }
2056 }
2057 }
2058 }
2059 }
2060
2061 if (!reportPcieLinkStats) {
2062 ALOGD("No PCIe link stats to report");
2063 return;
2064 }
2065
2066 // Load values array
2067 std::vector<VendorAtomValue> values(2 * ARRAY_SIZE(datamap));
2068 VendorAtomValue tmp;
2069 for (i=0; i < ARRAY_SIZE(datamap); i++) {
2070 if (datamap[i].modem_val > 0) {
2071 tmp.set<VendorAtomValue::intValue>(datamap[i].modem_val);
2072 values[datamap[i].modem_msg_field_number - kVendorAtomOffset] = tmp;
2073 }
2074 if (datamap[i].wifi_val > 0) {
2075 tmp.set<VendorAtomValue::intValue>(datamap[i].wifi_val);
2076 values[datamap[i].wifi_msg_field_number - kVendorAtomOffset] = tmp;
2077 }
2078 }
2079
2080 // Send vendor atom to IStats HAL
2081 VendorAtom event = {.reverseDomainName = "",
2082 .atomId = PixelAtoms::Atom::kPcieLinkStats,
2083 .values = std::move(values)};
2084
2085 const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
2086 if (!ret.isOk()) {
2087 ALOGE("Unable to report PCIe link statistics to stats service");
2088 }
2089 }
2090
2091 /**
2092 * Read the contents of kPowerMitigationDurationPath and report them.
2093 */
logMitigationDurationCounts(const std::shared_ptr<IStats> & stats_client)2094 void SysfsCollector::logMitigationDurationCounts(const std::shared_ptr<IStats> &stats_client) {
2095 if (kPowerMitigationDurationPath == nullptr || strlen(kPowerMitigationDurationPath) == 0) {
2096 ALOGE("Mitigation Duration path is invalid!");
2097 return;
2098 }
2099 mitigation_duration_reporter_.logMitigationDuration(stats_client, kPowerMitigationDurationPath);
2100 }
2101
logPerDay()2102 void SysfsCollector::logPerDay() {
2103 const std::shared_ptr<IStats> stats_client = getStatsService();
2104 if (!stats_client) {
2105 ALOGE("Unable to get AIDL Stats service");
2106 return;
2107 }
2108 // Collect once per service init; can be multiple due to service reinit
2109 if (!log_once_reported) {
2110 logBootStats(stats_client);
2111 }
2112 logBatteryCapacity(stats_client);
2113 logBatteryChargeCycles(stats_client);
2114 logBatteryEEPROM(stats_client);
2115 logBatteryHealth(stats_client);
2116 logBatteryTTF(stats_client);
2117 logBlockStatsReported(stats_client);
2118 logCodec1Failed(stats_client);
2119 logCodecFailed(stats_client);
2120 logDisplayStats(stats_client);
2121 logDisplayPortStats(stats_client);
2122 logHDCPStats(stats_client);
2123 logF2fsStats(stats_client);
2124 logF2fsAtomicWriteInfo(stats_client);
2125 logF2fsCompressionInfo(stats_client);
2126 logF2fsGcSegmentInfo(stats_client);
2127 logF2fsSmartIdleMaintEnabled(stats_client);
2128 logSlowIO(stats_client);
2129 logSpeakerImpedance(stats_client);
2130 logSpeechDspStat(stats_client);
2131 logUFSLifetime(stats_client);
2132 logUFSErrorStats(stats_client);
2133 logSpeakerHealthStats(stats_client);
2134 mm_metrics_reporter_.logCmaStatus(stats_client);
2135 mm_metrics_reporter_.logPixelMmMetricsPerDay(stats_client);
2136 logVendorAudioHardwareStats(stats_client);
2137 logThermalStats(stats_client);
2138 logTempResidencyStats(stats_client);
2139 logVendorLongIRQStatsReported(stats_client);
2140 logVendorResumeLatencyStats(stats_client);
2141 logPartitionUsedSpace(stats_client);
2142 logPcieLinkStats(stats_client);
2143 logMitigationDurationCounts(stats_client);
2144 logVendorAudioPdmStatsReported(stats_client);
2145 logWavesStats(stats_client);
2146 logAdaptedInfoStats(stats_client);
2147 logPcmUsageStats(stats_client);
2148 logOffloadEffectsStats(stats_client);
2149 logBluetoothAudioUsage(stats_client);
2150 }
2151
aggregatePer5Min()2152 void SysfsCollector::aggregatePer5Min() {
2153 mm_metrics_reporter_.aggregatePixelMmMetricsPer5Min();
2154 }
2155
logBrownout()2156 void SysfsCollector::logBrownout() {
2157 const std::shared_ptr<IStats> stats_client = getStatsService();
2158 if (!stats_client) {
2159 ALOGE("Unable to get AIDL Stats service");
2160 return;
2161 }
2162 if (kBrownoutCsvPath != nullptr && strlen(kBrownoutCsvPath) > 0)
2163 brownout_detected_reporter_.logBrownoutCsv(stats_client, kBrownoutCsvPath,
2164 kBrownoutReasonProp);
2165 else if (kBrownoutLogPath != nullptr && strlen(kBrownoutLogPath) > 0)
2166 brownout_detected_reporter_.logBrownout(stats_client, kBrownoutLogPath,
2167 kBrownoutReasonProp);
2168 }
2169
logOnce()2170 void SysfsCollector::logOnce() {
2171 logBrownout();
2172 logBatteryHistoryValidation();
2173 }
2174
logPerHour()2175 void SysfsCollector::logPerHour() {
2176 const std::shared_ptr<IStats> stats_client = getStatsService();
2177 if (!stats_client) {
2178 ALOGE("Unable to get AIDL Stats service");
2179 return;
2180 }
2181 mm_metrics_reporter_.logPixelMmMetricsPerHour(stats_client);
2182 logZramStats(stats_client);
2183 if (kPowerMitigationStatsPath != nullptr && strlen(kPowerMitigationStatsPath) > 0)
2184 mitigation_stats_reporter_.logMitigationStatsPerHour(stats_client,
2185 kPowerMitigationStatsPath);
2186 }
2187
2188 /**
2189 * Loop forever collecting stats from sysfs nodes and reporting them via
2190 * IStats.
2191 */
collect(void)2192 void SysfsCollector::collect(void) {
2193 int timerfd = timerfd_create(CLOCK_BOOTTIME, 0);
2194 if (timerfd < 0) {
2195 ALOGE("Unable to create timerfd - %s", strerror(errno));
2196 return;
2197 }
2198
2199 // Sleep for 30 seconds on launch to allow codec driver to load.
2200 sleep(30);
2201
2202 // sample & aggregate for the first time.
2203 aggregatePer5Min();
2204
2205 // Collect first set of stats on boot.
2206 logOnce();
2207 logPerHour();
2208 logPerDay();
2209
2210 struct itimerspec period;
2211
2212 // gcd (greatest common divisor) of all the following timings
2213 constexpr int kSecondsPerWake = 5 * 60;
2214
2215 constexpr int kWakesPer5Min = 5 * 60 / kSecondsPerWake;
2216 constexpr int kWakesPerHour = 60 * 60 / kSecondsPerWake;
2217 constexpr int kWakesPerDay = 24 * 60 * 60 / kSecondsPerWake;
2218
2219 int wake_5min = 0;
2220 int wake_hours = 0;
2221 int wake_days = 0;
2222
2223 period.it_interval.tv_sec = kSecondsPerWake;
2224 period.it_interval.tv_nsec = 0;
2225 period.it_value.tv_sec = kSecondsPerWake;
2226 period.it_value.tv_nsec = 0;
2227
2228 if (timerfd_settime(timerfd, 0, &period, NULL)) {
2229 ALOGE("Unable to set one hour timer - %s", strerror(errno));
2230 return;
2231 }
2232
2233 while (1) {
2234 int readval;
2235 union {
2236 char buf[8];
2237 uint64_t count;
2238 } expire;
2239
2240 do {
2241 errno = 0;
2242 readval = read(timerfd, expire.buf, sizeof(expire.buf));
2243 } while (readval < 0 && errno == EINTR);
2244 if (readval < 0) {
2245 ALOGE("Timerfd error - %s\n", strerror(errno));
2246 return;
2247 }
2248
2249 wake_5min += expire.count;
2250 wake_hours += expire.count;
2251 wake_days += expire.count;
2252
2253 if (wake_5min >= kWakesPer5Min) {
2254 wake_5min %= kWakesPer5Min;
2255 aggregatePer5Min();
2256 }
2257
2258 if (wake_hours >= kWakesPerHour) {
2259 if (wake_hours >= 2 * kWakesPerHour)
2260 ALOGW("Hourly wake: sleep too much: expire.count=%" PRId64, expire.count);
2261 wake_hours %= kWakesPerHour;
2262 logPerHour();
2263 }
2264
2265 if (wake_days >= kWakesPerDay) {
2266 if (wake_hours >= 2 * kWakesPerDay)
2267 ALOGW("Daily wake: sleep too much: expire.count=%" PRId64, expire.count);
2268 wake_days %= kWakesPerDay;
2269 logPerDay();
2270 }
2271 }
2272 }
2273
2274 } // namespace pixel
2275 } // namespace google
2276 } // namespace hardware
2277 } // namespace android
2278