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 <cinttypes>
33 #include <string>
34 
35 namespace android {
36 namespace hardware {
37 namespace google {
38 namespace pixel {
39 
40 using aidl::android::frameworks::stats::VendorAtom;
41 using aidl::android::frameworks::stats::VendorAtomValue;
42 using android::base::ReadFileToString;
43 using android::base::StartsWith;
44 using android::base::WriteStringToFile;
45 using android::hardware::google::pixel::PixelAtoms::BatteryCapacity;
46 using android::hardware::google::pixel::PixelAtoms::BootStatsInfo;
47 using android::hardware::google::pixel::PixelAtoms::F2fsCompressionInfo;
48 using android::hardware::google::pixel::PixelAtoms::F2fsGcSegmentInfo;
49 using android::hardware::google::pixel::PixelAtoms::F2fsStatsInfo;
50 using android::hardware::google::pixel::PixelAtoms::StorageUfsHealth;
51 using android::hardware::google::pixel::PixelAtoms::StorageUfsResetCount;
52 using android::hardware::google::pixel::PixelAtoms::VendorChargeCycles;
53 using android::hardware::google::pixel::PixelAtoms::VendorHardwareFailed;
54 using android::hardware::google::pixel::PixelAtoms::VendorSlowIo;
55 using android::hardware::google::pixel::PixelAtoms::VendorSpeakerImpedance;
56 using android::hardware::google::pixel::PixelAtoms::VendorSpeakerStatsReported;
57 using android::hardware::google::pixel::PixelAtoms::VendorSpeechDspStat;
58 using android::hardware::google::pixel::PixelAtoms::ZramBdStat;
59 using android::hardware::google::pixel::PixelAtoms::ZramMmStat;
60 
SysfsCollector(const struct SysfsPaths & sysfs_paths)61 SysfsCollector::SysfsCollector(const struct SysfsPaths &sysfs_paths)
62     : kSlowioReadCntPath(sysfs_paths.SlowioReadCntPath),
63       kSlowioWriteCntPath(sysfs_paths.SlowioWriteCntPath),
64       kSlowioUnmapCntPath(sysfs_paths.SlowioUnmapCntPath),
65       kSlowioSyncCntPath(sysfs_paths.SlowioSyncCntPath),
66       kCycleCountBinsPath(sysfs_paths.CycleCountBinsPath),
67       kImpedancePath(sysfs_paths.ImpedancePath),
68       kCodecPath(sysfs_paths.CodecPath),
69       kCodec1Path(sysfs_paths.Codec1Path),
70       kSpeechDspPath(sysfs_paths.SpeechDspPath),
71       kBatteryCapacityCC(sysfs_paths.BatteryCapacityCC),
72       kBatteryCapacityVFSOC(sysfs_paths.BatteryCapacityVFSOC),
73       kUFSLifetimeA(sysfs_paths.UFSLifetimeA),
74       kUFSLifetimeB(sysfs_paths.UFSLifetimeB),
75       kUFSLifetimeC(sysfs_paths.UFSLifetimeC),
76       kF2fsStatsPath(sysfs_paths.F2fsStatsPath),
77       kZramMmStatPath("/sys/block/zram0/mm_stat"),
78       kZramBdStatPath("/sys/block/zram0/bd_stat"),
79       kEEPROMPath(sysfs_paths.EEPROMPath),
80       kPowerMitigationStatsPath(sysfs_paths.MitigationPath),
81       kSpeakerTemperaturePath(sysfs_paths.SpeakerTemperaturePath),
82       kSpeakerExcursionPath(sysfs_paths.SpeakerExcursionPath),
83       kSpeakerHeartbeatPath(sysfs_paths.SpeakerHeartBeatPath),
84       kUFSErrStatsPath(sysfs_paths.UFSErrStatsPath) {}
85 
ReadFileToInt(const std::string & path,int * val)86 bool SysfsCollector::ReadFileToInt(const std::string &path, int *val) {
87     return ReadFileToInt(path.c_str(), val);
88 }
89 
ReadFileToInt(const char * const path,int * val)90 bool SysfsCollector::ReadFileToInt(const char *const path, int *val) {
91     std::string file_contents;
92 
93     if (!ReadFileToString(path, &file_contents)) {
94         ALOGE("Unable to read %s - %s", path, strerror(errno));
95         return false;
96     } else if (StartsWith(file_contents, "0x")) {
97         if (sscanf(file_contents.c_str(), "0x%x", val) != 1) {
98             ALOGE("Unable to convert %s to hex - %s", path, strerror(errno));
99             return false;
100         }
101     } else if (sscanf(file_contents.c_str(), "%d", val) != 1) {
102         ALOGE("Unable to convert %s to int - %s", path, strerror(errno));
103         return false;
104     }
105     return true;
106 }
107 
108 /**
109  * Read the contents of kCycleCountBinsPath and report them via IStats HAL.
110  * The contents are expected to be N buckets total, the nth of which indicates the
111  * number of times battery %-full has been increased with the n/N% full bucket.
112  */
logBatteryChargeCycles(const std::shared_ptr<IStats> & stats_client)113 void SysfsCollector::logBatteryChargeCycles(const std::shared_ptr<IStats> &stats_client) {
114     std::string file_contents;
115     int val;
116     if (kCycleCountBinsPath == nullptr || strlen(kCycleCountBinsPath) == 0) {
117         ALOGV("Battery charge cycle path not specified");
118         return;
119     }
120     if (!ReadFileToString(kCycleCountBinsPath, &file_contents)) {
121         ALOGE("Unable to read battery charge cycles %s - %s", kCycleCountBinsPath, strerror(errno));
122         return;
123     }
124 
125     const int32_t kChargeCyclesBucketsCount =
126             VendorChargeCycles::kCycleBucket10FieldNumber - kVendorAtomOffset + 1;
127     std::vector<int32_t> charge_cycles;
128     std::stringstream stream(file_contents);
129     while (stream >> val) {
130         charge_cycles.push_back(val);
131     }
132     if (charge_cycles.size() > kChargeCyclesBucketsCount) {
133         ALOGW("Got excessive battery charge cycles count %" PRIu64,
134               static_cast<uint64_t>(charge_cycles.size()));
135     } else {
136         // Push 0 for buckets that do not exist.
137         for (int bucketIdx = charge_cycles.size(); bucketIdx < kChargeCyclesBucketsCount;
138              ++bucketIdx) {
139             charge_cycles.push_back(0);
140         }
141     }
142 
143     std::replace(file_contents.begin(), file_contents.end(), ' ', ',');
144     reportChargeCycles(stats_client, charge_cycles);
145 }
146 
147 /**
148  * Read the contents of kEEPROMPath and report them.
149  */
logBatteryEEPROM(const std::shared_ptr<IStats> & stats_client)150 void SysfsCollector::logBatteryEEPROM(const std::shared_ptr<IStats> &stats_client) {
151     if (kEEPROMPath == nullptr || strlen(kEEPROMPath) == 0) {
152         ALOGV("Battery EEPROM path not specified");
153         return;
154     }
155 
156     battery_EEPROM_reporter_.checkAndReport(stats_client, kEEPROMPath);
157 }
158 
159 /**
160  * Check the codec for failures over the past 24hr.
161  */
logCodecFailed(const std::shared_ptr<IStats> & stats_client)162 void SysfsCollector::logCodecFailed(const std::shared_ptr<IStats> &stats_client) {
163     std::string file_contents;
164     if (kCodecPath == nullptr || strlen(kCodecPath) == 0) {
165         ALOGV("Audio codec path not specified");
166         return;
167     }
168     if (!ReadFileToString(kCodecPath, &file_contents)) {
169         ALOGE("Unable to read codec state %s - %s", kCodecPath, strerror(errno));
170         return;
171     }
172     if (file_contents == "0") {
173         return;
174     } else {
175         VendorHardwareFailed failure;
176         failure.set_hardware_type(VendorHardwareFailed::HARDWARE_FAILED_CODEC);
177         failure.set_hardware_location(0);
178         failure.set_failure_code(VendorHardwareFailed::COMPLETE);
179         reportHardwareFailed(stats_client, failure);
180     }
181 }
182 
183 /**
184  * Check the codec1 for failures over the past 24hr.
185  */
logCodec1Failed(const std::shared_ptr<IStats> & stats_client)186 void SysfsCollector::logCodec1Failed(const std::shared_ptr<IStats> &stats_client) {
187     std::string file_contents;
188     if (kCodec1Path == nullptr || strlen(kCodec1Path) == 0) {
189         ALOGV("Audio codec1 path not specified");
190         return;
191     }
192     if (!ReadFileToString(kCodec1Path, &file_contents)) {
193         ALOGE("Unable to read codec1 state %s - %s", kCodec1Path, strerror(errno));
194         return;
195     }
196     if (file_contents == "0") {
197         return;
198     } else {
199         ALOGE("%s report hardware fail", kCodec1Path);
200         VendorHardwareFailed failure;
201         failure.set_hardware_type(VendorHardwareFailed::HARDWARE_FAILED_CODEC);
202         failure.set_hardware_location(1);
203         failure.set_failure_code(VendorHardwareFailed::COMPLETE);
204         reportHardwareFailed(stats_client, failure);
205     }
206 }
207 
reportSlowIoFromFile(const std::shared_ptr<IStats> & stats_client,const char * path,const VendorSlowIo::IoOperation & operation_s)208 void SysfsCollector::reportSlowIoFromFile(const std::shared_ptr<IStats> &stats_client,
209                                           const char *path,
210                                           const VendorSlowIo::IoOperation &operation_s) {
211     std::string file_contents;
212     if (path == nullptr || strlen(path) == 0) {
213         ALOGV("slow_io path not specified");
214         return;
215     }
216     if (!ReadFileToString(path, &file_contents)) {
217         ALOGE("Unable to read slowio %s - %s", path, strerror(errno));
218         return;
219     } else {
220         int32_t slow_io_count = 0;
221         if (sscanf(file_contents.c_str(), "%d", &slow_io_count) != 1) {
222             ALOGE("Unable to parse %s from file %s to int.", file_contents.c_str(), path);
223         } else if (slow_io_count > 0) {
224             VendorSlowIo slow_io;
225             slow_io.set_operation(operation_s);
226             slow_io.set_count(slow_io_count);
227             reportSlowIo(stats_client, slow_io);
228         }
229         // Clear the stats
230         if (!android::base::WriteStringToFile("0", path, true)) {
231             ALOGE("Unable to clear SlowIO entry %s - %s", path, strerror(errno));
232         }
233     }
234 }
235 
236 /**
237  * Check for slow IO operations.
238  */
logSlowIO(const std::shared_ptr<IStats> & stats_client)239 void SysfsCollector::logSlowIO(const std::shared_ptr<IStats> &stats_client) {
240     reportSlowIoFromFile(stats_client, kSlowioReadCntPath, VendorSlowIo::READ);
241     reportSlowIoFromFile(stats_client, kSlowioWriteCntPath, VendorSlowIo::WRITE);
242     reportSlowIoFromFile(stats_client, kSlowioUnmapCntPath, VendorSlowIo::UNMAP);
243     reportSlowIoFromFile(stats_client, kSlowioSyncCntPath, VendorSlowIo::SYNC);
244 }
245 
246 /**
247  * Report the last-detected impedance of left & right speakers.
248  */
logSpeakerImpedance(const std::shared_ptr<IStats> & stats_client)249 void SysfsCollector::logSpeakerImpedance(const std::shared_ptr<IStats> &stats_client) {
250     std::string file_contents;
251     if (kImpedancePath == nullptr || strlen(kImpedancePath) == 0) {
252         ALOGV("Audio impedance path not specified");
253         return;
254     }
255     if (!ReadFileToString(kImpedancePath, &file_contents)) {
256         ALOGE("Unable to read impedance path %s", kImpedancePath);
257         return;
258     }
259 
260     float left, right;
261     if (sscanf(file_contents.c_str(), "%g,%g", &left, &right) != 2) {
262         ALOGE("Unable to parse speaker impedance %s", file_contents.c_str());
263         return;
264     }
265     VendorSpeakerImpedance left_obj;
266     left_obj.set_speaker_location(0);
267     left_obj.set_impedance(static_cast<int32_t>(left * 1000));
268 
269     VendorSpeakerImpedance right_obj;
270     right_obj.set_speaker_location(1);
271     right_obj.set_impedance(static_cast<int32_t>(right * 1000));
272 
273     reportSpeakerImpedance(stats_client, left_obj);
274     reportSpeakerImpedance(stats_client, right_obj);
275 }
276 
277 /**
278  * Report the last-detected impedance, temperature and heartbeats of left & right speakers.
279  */
logSpeakerHealthStats(const std::shared_ptr<IStats> & stats_client)280 void SysfsCollector::logSpeakerHealthStats(const std::shared_ptr<IStats> &stats_client) {
281     std::string file_contents;
282 
283     if (kImpedancePath == nullptr || strlen(kImpedancePath) == 0) {
284         ALOGD("Audio impedance path not specified");
285         return;
286     }
287 
288     if (kSpeakerTemperaturePath == nullptr || strlen(kSpeakerTemperaturePath) == 0) {
289         ALOGD("Audio speaker temperature path not specified");
290         return;
291     }
292 
293     if (kSpeakerHeartbeatPath == nullptr || strlen(kSpeakerHeartbeatPath) == 0) {
294         ALOGD("Audio speaker heartbeat path not specified");
295         return;
296     }
297 
298     if (kSpeakerExcursionPath == nullptr || strlen(kSpeakerExcursionPath) == 0) {
299         ALOGD("Audio speaker excursion path not specified");
300         return;
301     }
302 
303     float left_impedance_ohm, right_impedance_ohm;
304 
305     if (!ReadFileToString(kImpedancePath, &file_contents)) {
306         ALOGE("Unable to read speaker impedance path %s", kImpedancePath);
307         return;
308     }
309     if (sscanf(file_contents.c_str(), "%g,%g", &left_impedance_ohm, &right_impedance_ohm) != 2) {
310         ALOGE("Unable to parse speaker impedance %s", file_contents.c_str());
311         return;
312     }
313 
314     float left_temperature_C, right_temperature_C;
315     if (!ReadFileToString(kSpeakerTemperaturePath, &file_contents)) {
316         ALOGE("Unable to read speaker temperature path %s", kSpeakerTemperaturePath);
317         return;
318     }
319     if (sscanf(file_contents.c_str(), "%g,%g", &left_temperature_C, &right_temperature_C) != 2) {
320         ALOGE("Unable to parse speaker temperature %s", file_contents.c_str());
321         return;
322     }
323 
324     float left_excursion_mm, right_excursion_mm;
325     if (!ReadFileToString(kSpeakerExcursionPath, &file_contents)) {
326         ALOGE("Unable to read speaker excursion path %s", kSpeakerExcursionPath);
327         return;
328     }
329     if (sscanf(file_contents.c_str(), "%g,%g", &left_excursion_mm, &right_excursion_mm) != 2) {
330         ALOGE("Unable to parse speaker excursion %s", file_contents.c_str());
331         return;
332     }
333 
334     float left_heartbeat, right_heartbeat;
335     if (!ReadFileToString(kSpeakerHeartbeatPath, &file_contents)) {
336         ALOGE("Unable to read speaker heartbeat path %s", kSpeakerHeartbeatPath);
337         return;
338     }
339     if (sscanf(file_contents.c_str(), "%g,%g", &left_heartbeat, &right_heartbeat) != 2) {
340         ALOGE("Unable to parse speaker heartbeat %s", file_contents.c_str());
341         return;
342     }
343 
344     VendorSpeakerStatsReported left_obj;
345     left_obj.set_speaker_location(0);
346     left_obj.set_impedance(static_cast<int32_t>(left_impedance_ohm * 1000));
347     left_obj.set_max_temperature(static_cast<int32_t>(left_temperature_C * 1000));
348     left_obj.set_excursion(static_cast<int32_t>(left_excursion_mm * 1000));
349     left_obj.set_heartbeat(static_cast<int32_t>(left_heartbeat));
350 
351     VendorSpeakerStatsReported right_obj;
352     right_obj.set_speaker_location(1);
353     right_obj.set_impedance(static_cast<int32_t>(right_impedance_ohm * 1000));
354     right_obj.set_max_temperature(static_cast<int32_t>(right_temperature_C * 1000));
355     right_obj.set_excursion(static_cast<int32_t>(right_excursion_mm * 1000));
356     right_obj.set_heartbeat(static_cast<int32_t>(right_heartbeat));
357 
358     reportSpeakerHealthStat(stats_client, left_obj);
359     reportSpeakerHealthStat(stats_client, right_obj);
360 }
361 
362 /**
363  * Report the Speech DSP state.
364  */
logSpeechDspStat(const std::shared_ptr<IStats> & stats_client)365 void SysfsCollector::logSpeechDspStat(const std::shared_ptr<IStats> &stats_client) {
366     std::string file_contents;
367     if (kSpeechDspPath == nullptr || strlen(kSpeechDspPath) == 0) {
368         ALOGV("Speech DSP path not specified");
369         return;
370     }
371     if (!ReadFileToString(kSpeechDspPath, &file_contents)) {
372         ALOGE("Unable to read speech dsp path %s", kSpeechDspPath);
373         return;
374     }
375 
376     int32_t up_time = 0, down_time = 0, crash_count = 0, recover_count = 0;
377     if (sscanf(file_contents.c_str(), "%d,%d,%d,%d", &up_time, &down_time, &crash_count,
378                &recover_count) != 4) {
379         ALOGE("Unable to parse speech dsp stat %s", file_contents.c_str());
380         return;
381     }
382 
383     ALOGD("SpeechDSP uptime %d downtime %d crashcount %d recovercount %d", up_time, down_time,
384           crash_count, recover_count);
385     VendorSpeechDspStat dsp_stat;
386     dsp_stat.set_total_uptime_millis(up_time);
387     dsp_stat.set_total_downtime_millis(down_time);
388     dsp_stat.set_total_crash_count(crash_count);
389     dsp_stat.set_total_recover_count(recover_count);
390 
391     reportSpeechDspStat(stats_client, dsp_stat);
392 }
393 
logBatteryCapacity(const std::shared_ptr<IStats> & stats_client)394 void SysfsCollector::logBatteryCapacity(const std::shared_ptr<IStats> &stats_client) {
395     std::string file_contents;
396     if (kBatteryCapacityCC == nullptr || strlen(kBatteryCapacityCC) == 0) {
397         ALOGV("Battery Capacity CC path not specified");
398         return;
399     }
400     if (kBatteryCapacityVFSOC == nullptr || strlen(kBatteryCapacityVFSOC) == 0) {
401         ALOGV("Battery Capacity VFSOC path not specified");
402         return;
403     }
404     int delta_cc_sum, delta_vfsoc_sum;
405     if (!ReadFileToInt(kBatteryCapacityCC, &delta_cc_sum) ||
406             !ReadFileToInt(kBatteryCapacityVFSOC, &delta_vfsoc_sum))
407         return;
408 
409     // Load values array
410     std::vector<VendorAtomValue> values(2);
411     VendorAtomValue tmp;
412     tmp.set<VendorAtomValue::intValue>(delta_cc_sum);
413     values[BatteryCapacity::kDeltaCcSumFieldNumber - kVendorAtomOffset] = tmp;
414     tmp.set<VendorAtomValue::intValue>(delta_vfsoc_sum);
415     values[BatteryCapacity::kDeltaVfsocSumFieldNumber - kVendorAtomOffset] = tmp;
416 
417     // Send vendor atom to IStats HAL
418     VendorAtom event = {.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
419                         .atomId = PixelAtoms::Atom::kBatteryCapacity,
420                         .values = std::move(values)};
421     const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
422     if (!ret.isOk())
423         ALOGE("Unable to report ChargeStats to Stats service");
424 }
425 
logUFSLifetime(const std::shared_ptr<IStats> & stats_client)426 void SysfsCollector::logUFSLifetime(const std::shared_ptr<IStats> &stats_client) {
427     std::string file_contents;
428     if (kUFSLifetimeA == nullptr || strlen(kUFSLifetimeA) == 0) {
429         ALOGV("UFS lifetimeA path not specified");
430         return;
431     }
432     if (kUFSLifetimeB == nullptr || strlen(kUFSLifetimeB) == 0) {
433         ALOGV("UFS lifetimeB path not specified");
434         return;
435     }
436     if (kUFSLifetimeC == nullptr || strlen(kUFSLifetimeC) == 0) {
437         ALOGV("UFS lifetimeC path not specified");
438         return;
439     }
440 
441     int lifetimeA = 0, lifetimeB = 0, lifetimeC = 0;
442     if (!ReadFileToInt(kUFSLifetimeA, &lifetimeA) ||
443         !ReadFileToInt(kUFSLifetimeB, &lifetimeB) ||
444         !ReadFileToInt(kUFSLifetimeC, &lifetimeC)) {
445         ALOGE("Unable to read UFS lifetime : %s", strerror(errno));
446         return;
447     }
448 
449     // Load values array
450     std::vector<VendorAtomValue> values(3);
451     VendorAtomValue tmp;
452     tmp.set<VendorAtomValue::intValue>(lifetimeA);
453     values[StorageUfsHealth::kLifetimeAFieldNumber - kVendorAtomOffset] = tmp;
454     tmp.set<VendorAtomValue::intValue>(lifetimeB);
455     values[StorageUfsHealth::kLifetimeBFieldNumber - kVendorAtomOffset] = tmp;
456     tmp.set<VendorAtomValue::intValue>(lifetimeC);
457     values[StorageUfsHealth::kLifetimeCFieldNumber - kVendorAtomOffset] = tmp;
458 
459     // Send vendor atom to IStats HAL
460     VendorAtom event = {.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
461                         .atomId = PixelAtoms::Atom::kStorageUfsHealth,
462                         .values = std::move(values)};
463     const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
464     if (!ret.isOk()) {
465         ALOGE("Unable to report UfsHealthStat to Stats service");
466     }
467 }
468 
logUFSErrorStats(const std::shared_ptr<IStats> & stats_client)469 void SysfsCollector::logUFSErrorStats(const std::shared_ptr<IStats> &stats_client) {
470     int value, host_reset_count = 0;
471 
472     if (kUFSErrStatsPath.empty() || strlen(kUFSErrStatsPath.front().c_str()) == 0) {
473         ALOGV("UFS host reset count specified");
474         return;
475     }
476 
477     for (int i = 0; i < kUFSErrStatsPath.size(); i++) {
478         if (!ReadFileToInt(kUFSErrStatsPath[i], &value)) {
479             ALOGE("Unable to read host reset count");
480             return;
481         }
482         host_reset_count += value;
483     }
484 
485     // Load values array
486     std::vector<VendorAtomValue> values(1);
487     VendorAtomValue tmp;
488     tmp.set<VendorAtomValue::intValue>(host_reset_count);
489     values[StorageUfsResetCount::kHostResetCountFieldNumber - kVendorAtomOffset] = tmp;
490 
491     // Send vendor atom to IStats HAL
492     VendorAtom event = {.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
493                         .atomId = PixelAtoms::Atom::kUfsResetCount,
494                         .values = std::move(values)};
495     const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
496     if (!ret.isOk()) {
497         ALOGE("Unable to report UFS host reset count to Stats service");
498     }
499 }
500 
getUserDataBlock()501 static std::string getUserDataBlock() {
502     std::unique_ptr<std::FILE, int (*)(std::FILE*)> fp(setmntent("/proc/mounts", "re"), endmntent);
503     if (fp == nullptr) {
504         ALOGE("Error opening /proc/mounts");
505         return "";
506     }
507 
508     mntent* mentry;
509     while ((mentry = getmntent(fp.get())) != nullptr) {
510         if (strcmp(mentry->mnt_dir, "/data") == 0) {
511             return std::string(basename(mentry->mnt_fsname));
512         }
513     }
514     return "";
515 }
516 
logF2fsStats(const std::shared_ptr<IStats> & stats_client)517 void SysfsCollector::logF2fsStats(const std::shared_ptr<IStats> &stats_client) {
518     int dirty, free, cp_calls_fg, gc_calls_fg, moved_block_fg, vblocks;
519     int cp_calls_bg, gc_calls_bg, moved_block_bg;
520 
521     if (kF2fsStatsPath == nullptr) {
522         ALOGE("F2fs stats path not specified");
523         return;
524     }
525 
526     const std::string userdataBlock = getUserDataBlock();
527     const std::string kF2fsStatsDir = kF2fsStatsPath + userdataBlock;
528 
529     if (!ReadFileToInt(kF2fsStatsDir + "/dirty_segments", &dirty)) {
530         ALOGV("Unable to read dirty segments");
531     }
532 
533     if (!ReadFileToInt(kF2fsStatsDir + "/free_segments", &free)) {
534         ALOGV("Unable to read free segments");
535     }
536 
537     if (!ReadFileToInt(kF2fsStatsDir + "/cp_foreground_calls", &cp_calls_fg)) {
538         ALOGV("Unable to read cp_foreground_calls");
539     }
540 
541     if (!ReadFileToInt(kF2fsStatsDir + "/cp_background_calls", &cp_calls_bg)) {
542         ALOGV("Unable to read cp_background_calls");
543     }
544 
545     if (!ReadFileToInt(kF2fsStatsDir + "/gc_foreground_calls", &gc_calls_fg)) {
546         ALOGV("Unable to read gc_foreground_calls");
547     }
548 
549     if (!ReadFileToInt(kF2fsStatsDir + "/gc_background_calls", &gc_calls_bg)) {
550         ALOGV("Unable to read gc_background_calls");
551     }
552 
553     if (!ReadFileToInt(kF2fsStatsDir + "/moved_blocks_foreground", &moved_block_fg)) {
554         ALOGV("Unable to read moved_blocks_foreground");
555     }
556 
557     if (!ReadFileToInt(kF2fsStatsDir + "/moved_blocks_background", &moved_block_bg)) {
558         ALOGV("Unable to read moved_blocks_background");
559     }
560 
561     if (!ReadFileToInt(kF2fsStatsDir + "/avg_vblocks", &vblocks)) {
562         ALOGV("Unable to read avg_vblocks");
563     }
564 
565     // Load values array
566     std::vector<VendorAtomValue> values(9);
567     VendorAtomValue tmp;
568     tmp.set<VendorAtomValue::intValue>(dirty);
569     values[F2fsStatsInfo::kDirtySegmentsFieldNumber - kVendorAtomOffset] = tmp;
570     tmp.set<VendorAtomValue::intValue>(free);
571     values[F2fsStatsInfo::kFreeSegmentsFieldNumber - kVendorAtomOffset] = tmp;
572     tmp.set<VendorAtomValue::intValue>(cp_calls_fg);
573     values[F2fsStatsInfo::kCpCallsFgFieldNumber - kVendorAtomOffset] = tmp;
574     tmp.set<VendorAtomValue::intValue>(cp_calls_bg);
575     values[F2fsStatsInfo::kCpCallsBgFieldNumber - kVendorAtomOffset] = tmp;
576     tmp.set<VendorAtomValue::intValue>(gc_calls_fg);
577     values[F2fsStatsInfo::kGcCallsFgFieldNumber - kVendorAtomOffset] = tmp;
578     tmp.set<VendorAtomValue::intValue>(gc_calls_bg);
579     values[F2fsStatsInfo::kGcCallsBgFieldNumber - kVendorAtomOffset] = tmp;
580     tmp.set<VendorAtomValue::intValue>(moved_block_fg);
581     values[F2fsStatsInfo::kMovedBlocksFgFieldNumber - kVendorAtomOffset] = tmp;
582     tmp.set<VendorAtomValue::intValue>(moved_block_bg);
583     values[F2fsStatsInfo::kMovedBlocksBgFieldNumber - kVendorAtomOffset] = tmp;
584     tmp.set<VendorAtomValue::intValue>(vblocks);
585     values[F2fsStatsInfo::kValidBlocksFieldNumber - kVendorAtomOffset] = tmp;
586 
587     // Send vendor atom to IStats HAL
588     VendorAtom event = {.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
589                         .atomId = PixelAtoms::Atom::kF2FsStats,
590                         .values = std::move(values)};
591     const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
592     if (!ret.isOk()) {
593         ALOGE("Unable to report F2fs stats to Stats service");
594     }
595 }
596 
logF2fsCompressionInfo(const std::shared_ptr<IStats> & stats_client)597 void SysfsCollector::logF2fsCompressionInfo(const std::shared_ptr<IStats> &stats_client) {
598     int compr_written_blocks, compr_saved_blocks, compr_new_inodes;
599 
600     if (kF2fsStatsPath == nullptr) {
601         ALOGV("F2fs stats path not specified");
602         return;
603     }
604 
605     std::string userdataBlock = getUserDataBlock();
606 
607     std::string path = kF2fsStatsPath + (userdataBlock + "/compr_written_block");
608     if (!ReadFileToInt(path, &compr_written_blocks)) {
609         ALOGE("Unable to read compression written blocks");
610         return;
611     }
612 
613     path = kF2fsStatsPath + (userdataBlock + "/compr_saved_block");
614     if (!ReadFileToInt(path, &compr_saved_blocks)) {
615         ALOGE("Unable to read compression saved blocks");
616         return;
617     } else {
618         if (!WriteStringToFile(std::to_string(0), path)) {
619             ALOGE("Failed to write to file %s", path.c_str());
620             return;
621         }
622     }
623 
624     path = kF2fsStatsPath + (userdataBlock + "/compr_new_inode");
625     if (!ReadFileToInt(path, &compr_new_inodes)) {
626         ALOGE("Unable to read compression new inodes");
627         return;
628     } else {
629         if (!WriteStringToFile(std::to_string(0), path)) {
630             ALOGE("Failed to write to file %s", path.c_str());
631             return;
632         }
633     }
634 
635     // Load values array
636     std::vector<VendorAtomValue> values(3);
637     VendorAtomValue tmp;
638     tmp.set<VendorAtomValue::intValue>(compr_written_blocks);
639     values[F2fsCompressionInfo::kComprWrittenBlocksFieldNumber - kVendorAtomOffset] = tmp;
640     tmp.set<VendorAtomValue::intValue>(compr_saved_blocks);
641     values[F2fsCompressionInfo::kComprSavedBlocksFieldNumber - kVendorAtomOffset] = tmp;
642     tmp.set<VendorAtomValue::intValue>(compr_new_inodes);
643     values[F2fsCompressionInfo::kComprNewInodesFieldNumber - kVendorAtomOffset] = tmp;
644 
645     // Send vendor atom to IStats HAL
646     VendorAtom event = {.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
647                         .atomId = PixelAtoms::Atom::kF2FsCompressionInfo,
648                         .values = values};
649     const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
650     if (!ret.isOk()) {
651         ALOGE("Unable to report F2fs compression info to Stats service");
652     }
653 }
654 
getReclaimedSegments(const std::string & mode)655 int SysfsCollector::getReclaimedSegments(const std::string &mode) {
656     std::string userDataStatsPath = kF2fsStatsPath + getUserDataBlock();
657     std::string gcSegmentModePath = userDataStatsPath + "/gc_segment_mode";
658     std::string gcReclaimedSegmentsPath = userDataStatsPath + "/gc_reclaimed_segments";
659     int reclaimed_segments;
660 
661     if (!WriteStringToFile(mode, gcSegmentModePath)) {
662         ALOGE("Failed to change gc_segment_mode to %s", mode.c_str());
663         return -1;
664     }
665 
666     if (!ReadFileToInt(gcReclaimedSegmentsPath, &reclaimed_segments)) {
667         ALOGE("GC mode(%s): Unable to read gc_reclaimed_segments", mode.c_str());
668         return -1;
669     }
670 
671     if (!WriteStringToFile(std::to_string(0), gcReclaimedSegmentsPath)) {
672         ALOGE("GC mode(%s): Failed to reset gc_reclaimed_segments", mode.c_str());
673         return -1;
674     }
675 
676     return reclaimed_segments;
677 }
678 
logF2fsGcSegmentInfo(const std::shared_ptr<IStats> & stats_client)679 void SysfsCollector::logF2fsGcSegmentInfo(const std::shared_ptr<IStats> &stats_client) {
680     int reclaimed_segments_normal, reclaimed_segments_urgent_high, reclaimed_segments_urgent_low;
681     std::string gc_normal_mode = std::to_string(0);         // GC normal mode
682     std::string gc_urgent_high_mode = std::to_string(4);    // GC urgent high mode
683     std::string gc_urgent_low_mode = std::to_string(5);     // GC urgent low mode
684 
685     if (kF2fsStatsPath == nullptr) {
686         ALOGV("F2fs stats path not specified");
687         return;
688     }
689 
690     reclaimed_segments_normal = getReclaimedSegments(gc_normal_mode);
691     if (reclaimed_segments_normal == -1) return;
692     reclaimed_segments_urgent_high = getReclaimedSegments(gc_urgent_high_mode);
693     if (reclaimed_segments_urgent_high == -1) return;
694     reclaimed_segments_urgent_low = getReclaimedSegments(gc_urgent_low_mode);
695     if (reclaimed_segments_urgent_low == -1) return;
696 
697     // Load values array
698     std::vector<VendorAtomValue> values(3);
699     VendorAtomValue tmp;
700     tmp.set<VendorAtomValue::intValue>(reclaimed_segments_normal);
701     values[F2fsGcSegmentInfo::kReclaimedSegmentsNormalFieldNumber - kVendorAtomOffset] = tmp;
702     tmp.set<VendorAtomValue::intValue>(reclaimed_segments_urgent_high);
703     values[F2fsGcSegmentInfo::kReclaimedSegmentsUrgentHighFieldNumber - kVendorAtomOffset] = tmp;
704     tmp.set<VendorAtomValue::intValue>(reclaimed_segments_urgent_low);
705     values[F2fsGcSegmentInfo::kReclaimedSegmentsUrgentLowFieldNumber - kVendorAtomOffset] = tmp;
706 
707     // Send vendor atom to IStats HAL
708     VendorAtom event = {.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
709                         .atomId = PixelAtoms::Atom::kF2FsGcSegmentInfo,
710                         .values = values};
711     const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
712     if (!ret.isOk()) {
713         ALOGE("Unable to report F2fs GC Segment info to Stats service");
714     }
715 }
716 
reportZramMmStat(const std::shared_ptr<IStats> & stats_client)717 void SysfsCollector::reportZramMmStat(const std::shared_ptr<IStats> &stats_client) {
718     std::string file_contents;
719     if (!kZramMmStatPath) {
720         ALOGV("ZramMmStat path not specified");
721         return;
722     }
723 
724     if (!ReadFileToString(kZramMmStatPath, &file_contents)) {
725         ALOGE("Unable to ZramMmStat %s - %s", kZramMmStatPath, strerror(errno));
726         return;
727     } else {
728         int64_t orig_data_size = 0;
729         int64_t compr_data_size = 0;
730         int64_t mem_used_total = 0;
731         int64_t mem_limit = 0;
732         int64_t max_used_total = 0;
733         int64_t same_pages = 0;
734         int64_t pages_compacted = 0;
735         int64_t huge_pages = 0;
736         int64_t huge_pages_since_boot = 0;
737 
738         // huge_pages_since_boot may not exist according to kernel version.
739         // only check if the number of collected data is equal or larger then 8
740         if (sscanf(file_contents.c_str(),
741                    "%" SCNd64 " %" SCNd64 " %" SCNd64 " %" SCNd64 " %" SCNd64 " %" SCNd64
742                    " %" SCNd64 " %" SCNd64 " %" SCNd64,
743                    &orig_data_size, &compr_data_size, &mem_used_total, &mem_limit, &max_used_total,
744                    &same_pages, &pages_compacted, &huge_pages, &huge_pages_since_boot) < 8) {
745             ALOGE("Unable to parse ZramMmStat %s from file %s to int.",
746                     file_contents.c_str(), kZramMmStatPath);
747         }
748 
749         // Load values array.
750         // The size should be the same as the number of fields in ZramMmStat
751         std::vector<VendorAtomValue> values(6);
752         VendorAtomValue tmp;
753         tmp.set<VendorAtomValue::intValue>(orig_data_size);
754         values[ZramMmStat::kOrigDataSizeFieldNumber - kVendorAtomOffset] = tmp;
755         tmp.set<VendorAtomValue::intValue>(compr_data_size);
756         values[ZramMmStat::kComprDataSizeFieldNumber - kVendorAtomOffset] = tmp;
757         tmp.set<VendorAtomValue::intValue>(mem_used_total);
758         values[ZramMmStat::kMemUsedTotalFieldNumber - kVendorAtomOffset] = tmp;
759         tmp.set<VendorAtomValue::intValue>(same_pages);
760         values[ZramMmStat::kSamePagesFieldNumber - kVendorAtomOffset] = tmp;
761         tmp.set<VendorAtomValue::intValue>(huge_pages);
762         values[ZramMmStat::kHugePagesFieldNumber - kVendorAtomOffset] = tmp;
763 
764         // Skip the first data to avoid a big spike in this accumulated value.
765         if (prev_huge_pages_since_boot_ == -1)
766             tmp.set<VendorAtomValue::intValue>(0);
767         else
768             tmp.set<VendorAtomValue::intValue>(huge_pages_since_boot - prev_huge_pages_since_boot_);
769 
770         values[ZramMmStat::kHugePagesSinceBootFieldNumber - kVendorAtomOffset] = tmp;
771         prev_huge_pages_since_boot_ = huge_pages_since_boot;
772 
773         // Send vendor atom to IStats HAL
774         VendorAtom event = {.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
775                             .atomId = PixelAtoms::Atom::kZramMmStat,
776                             .values = std::move(values)};
777         const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
778         if (!ret.isOk())
779             ALOGE("Zram Unable to report ZramMmStat to Stats service");
780     }
781 }
782 
reportZramBdStat(const std::shared_ptr<IStats> & stats_client)783 void SysfsCollector::reportZramBdStat(const std::shared_ptr<IStats> &stats_client) {
784     std::string file_contents;
785     if (!kZramBdStatPath) {
786         ALOGV("ZramBdStat path not specified");
787         return;
788     }
789 
790     if (!ReadFileToString(kZramBdStatPath, &file_contents)) {
791         ALOGE("Unable to ZramBdStat %s - %s", kZramBdStatPath, strerror(errno));
792         return;
793     } else {
794         int64_t bd_count = 0;
795         int64_t bd_reads = 0;
796         int64_t bd_writes = 0;
797 
798         if (sscanf(file_contents.c_str(), "%" SCNd64 " %" SCNd64 " %" SCNd64,
799                                 &bd_count, &bd_reads, &bd_writes) != 3) {
800             ALOGE("Unable to parse ZramBdStat %s from file %s to int.",
801                     file_contents.c_str(), kZramBdStatPath);
802         }
803 
804         // Load values array
805         std::vector<VendorAtomValue> values(3);
806         VendorAtomValue tmp;
807         tmp.set<VendorAtomValue::intValue>(bd_count);
808         values[ZramBdStat::kBdCountFieldNumber - kVendorAtomOffset] = tmp;
809         tmp.set<VendorAtomValue::intValue>(bd_reads);
810         values[ZramBdStat::kBdReadsFieldNumber - kVendorAtomOffset] = tmp;
811         tmp.set<VendorAtomValue::intValue>(bd_writes);
812         values[ZramBdStat::kBdWritesFieldNumber - kVendorAtomOffset] = tmp;
813 
814         // Send vendor atom to IStats HAL
815         VendorAtom event = {.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
816                             .atomId = PixelAtoms::Atom::kZramBdStat,
817                             .values = std::move(values)};
818         const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
819         if (!ret.isOk())
820             ALOGE("Zram Unable to report ZramBdStat to Stats service");
821     }
822 }
823 
logZramStats(const std::shared_ptr<IStats> & stats_client)824 void SysfsCollector::logZramStats(const std::shared_ptr<IStats> &stats_client) {
825     reportZramMmStat(stats_client);
826     reportZramBdStat(stats_client);
827 }
828 
logBootStats(const std::shared_ptr<IStats> & stats_client)829 void SysfsCollector::logBootStats(const std::shared_ptr<IStats> &stats_client) {
830     int mounted_time_sec = 0;
831 
832     if (kF2fsStatsPath == nullptr) {
833         ALOGE("F2fs stats path not specified");
834         return;
835     }
836 
837     std::string userdataBlock = getUserDataBlock();
838 
839     if (!ReadFileToInt(kF2fsStatsPath + (userdataBlock + "/mounted_time_sec"), &mounted_time_sec)) {
840         ALOGV("Unable to read mounted_time_sec");
841         return;
842     }
843 
844     int fsck_time_ms = android::base::GetIntProperty("ro.boottime.init.fsck.data", 0);
845     int checkpoint_time_ms = android::base::GetIntProperty("ro.boottime.init.mount.data", 0);
846 
847     if (fsck_time_ms == 0 && checkpoint_time_ms == 0) {
848         ALOGV("Not yet initialized");
849         return;
850     }
851 
852     // Load values array
853     std::vector<VendorAtomValue> values(3);
854     VendorAtomValue tmp;
855     tmp.set<VendorAtomValue::intValue>(mounted_time_sec);
856     values[BootStatsInfo::kMountedTimeSecFieldNumber - kVendorAtomOffset] = tmp;
857     tmp.set<VendorAtomValue::intValue>(fsck_time_ms / 1000);
858     values[BootStatsInfo::kFsckTimeSecFieldNumber - kVendorAtomOffset] = tmp;
859     tmp.set<VendorAtomValue::intValue>(checkpoint_time_ms / 1000);
860     values[BootStatsInfo::kCheckpointTimeSecFieldNumber - kVendorAtomOffset] = tmp;
861 
862     // Send vendor atom to IStats HAL
863     VendorAtom event = {.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
864                         .atomId = PixelAtoms::Atom::kBootStats,
865                         .values = std::move(values)};
866     const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
867     if (!ret.isOk()) {
868         ALOGE("Unable to report Boot stats to Stats service");
869     } else {
870         log_once_reported = true;
871     }
872 }
873 
logPerDay()874 void SysfsCollector::logPerDay() {
875     const std::shared_ptr<IStats> stats_client = getStatsService();
876     if (!stats_client) {
877         ALOGE("Unable to get AIDL Stats service");
878         return;
879     }
880     // Collect once per service init; can be multiple due to service reinit
881     if (!log_once_reported) {
882         logBootStats(stats_client);
883     }
884     logBatteryCapacity(stats_client);
885     logBatteryChargeCycles(stats_client);
886     logBatteryEEPROM(stats_client);
887     logCodec1Failed(stats_client);
888     logCodecFailed(stats_client);
889     logF2fsStats(stats_client);
890     logF2fsCompressionInfo(stats_client);
891     logF2fsGcSegmentInfo(stats_client);
892     logSlowIO(stats_client);
893     logSpeakerImpedance(stats_client);
894     logSpeechDspStat(stats_client);
895     logUFSLifetime(stats_client);
896     logUFSErrorStats(stats_client);
897     logSpeakerHealthStats(stats_client);
898     mm_metrics_reporter_.logCmaStatus(stats_client);
899     mm_metrics_reporter_.logPixelMmMetricsPerDay(stats_client);
900 }
901 
logPerHour()902 void SysfsCollector::logPerHour() {
903     const std::shared_ptr<IStats> stats_client = getStatsService();
904     if (!stats_client) {
905         ALOGE("Unable to get AIDL Stats service");
906         return;
907     }
908     mm_metrics_reporter_.logPixelMmMetricsPerHour(stats_client);
909     logZramStats(stats_client);
910     if (kPowerMitigationStatsPath != nullptr && strlen(kPowerMitigationStatsPath) > 0)
911         mitigation_stats_reporter_.logMitigationStatsPerHour(stats_client,
912                                                              kPowerMitigationStatsPath);
913 }
914 
915 /**
916  * Loop forever collecting stats from sysfs nodes and reporting them via
917  * IStats.
918  */
collect(void)919 void SysfsCollector::collect(void) {
920     int timerfd = timerfd_create(CLOCK_BOOTTIME, 0);
921     if (timerfd < 0) {
922         ALOGE("Unable to create timerfd - %s", strerror(errno));
923         return;
924     }
925 
926     // Sleep for 30 seconds on launch to allow codec driver to load.
927     sleep(30);
928 
929     // Collect first set of stats on boot.
930     logPerHour();
931     logPerDay();
932 
933     // Set an one-hour timer.
934     struct itimerspec period;
935     const int kSecondsPerHour = 60 * 60;
936     int hours = 0;
937     period.it_interval.tv_sec = kSecondsPerHour;
938     period.it_interval.tv_nsec = 0;
939     period.it_value.tv_sec = kSecondsPerHour;
940     period.it_value.tv_nsec = 0;
941 
942     if (timerfd_settime(timerfd, 0, &period, NULL)) {
943         ALOGE("Unable to set one hour timer - %s", strerror(errno));
944         return;
945     }
946 
947     while (1) {
948         int readval;
949         do {
950             char buf[8];
951             errno = 0;
952             readval = read(timerfd, buf, sizeof(buf));
953         } while (readval < 0 && errno == EINTR);
954         if (readval < 0) {
955             ALOGE("Timerfd error - %s\n", strerror(errno));
956             return;
957         }
958 
959         hours++;
960         logPerHour();
961         if (hours == 24) {
962             // Collect stats every 24hrs after.
963             logPerDay();
964             hours = 0;
965         }
966     }
967 }
968 
969 }  // namespace pixel
970 }  // namespace google
971 }  // namespace hardware
972 }  // namespace android
973