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