• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/SysfsCollector.h>
18 
19 #define LOG_TAG "pixelstats-vendor"
20 
21 #include <android-base/file.h>
22 #include <android-base/parseint.h>
23 #include <android-base/properties.h>
24 #include <android-base/strings.h>
25 #include <android/frameworks/stats/1.0/IStats.h>
26 #include <hardware/google/pixel/pixelstats/pixelatoms.pb.h>
27 #include <utils/Log.h>
28 #include <utils/StrongPointer.h>
29 #include <utils/Timers.h>
30 
31 #include <sys/timerfd.h>
32 #include <mntent.h>
33 #include <string>
34 
35 namespace android {
36 namespace hardware {
37 namespace google {
38 namespace pixel {
39 
40 using android::sp;
41 using android::base::ReadFileToString;
42 using android::base::StartsWith;
43 using android::frameworks::stats::V1_0::ChargeCycles;
44 using android::frameworks::stats::V1_0::HardwareFailed;
45 using android::frameworks::stats::V1_0::IStats;
46 using android::frameworks::stats::V1_0::SlowIo;
47 using android::frameworks::stats::V1_0::SpeakerImpedance;
48 using android::frameworks::stats::V1_0::SpeechDspStat;
49 using android::frameworks::stats::V1_0::VendorAtom;
50 using android::hardware::google::pixel::PixelAtoms::BatteryCapacity;
51 using android::hardware::google::pixel::PixelAtoms::StorageUfsHealth;
52 using android::hardware::google::pixel::PixelAtoms::F2fsStatsInfo;
53 using android::hardware::google::pixel::PixelAtoms::ZramMmStat;
54 using android::hardware::google::pixel::PixelAtoms::ZramBdStat;
55 using android::hardware::google::pixel::PixelAtoms::BootStatsInfo;
56 
SysfsCollector(const struct SysfsPaths & sysfs_paths)57 SysfsCollector::SysfsCollector(const struct SysfsPaths &sysfs_paths)
58     : kSlowioReadCntPath(sysfs_paths.SlowioReadCntPath),
59       kSlowioWriteCntPath(sysfs_paths.SlowioWriteCntPath),
60       kSlowioUnmapCntPath(sysfs_paths.SlowioUnmapCntPath),
61       kSlowioSyncCntPath(sysfs_paths.SlowioSyncCntPath),
62       kCycleCountBinsPath(sysfs_paths.CycleCountBinsPath),
63       kImpedancePath(sysfs_paths.ImpedancePath),
64       kCodecPath(sysfs_paths.CodecPath),
65       kCodec1Path(sysfs_paths.Codec1Path),
66       kSpeechDspPath(sysfs_paths.SpeechDspPath),
67       kBatteryCapacityCC(sysfs_paths.BatteryCapacityCC),
68       kBatteryCapacityVFSOC(sysfs_paths.BatteryCapacityVFSOC),
69       kUFSLifetimeA(sysfs_paths.UFSLifetimeA),
70       kUFSLifetimeB(sysfs_paths.UFSLifetimeB),
71       kUFSLifetimeC(sysfs_paths.UFSLifetimeC),
72       kF2fsStatsPath(sysfs_paths.F2fsStatsPath),
73       kUserdataBlockProp(sysfs_paths.UserdataBlockProp),
74       kZramMmStatPath("/sys/block/zram0/mm_stat"),
75       kZramBdStatPath("/sys/block/zram0/bd_stat"),
76       kEEPROMPath(sysfs_paths.EEPROMPath) {}
77 
ReadFileToInt(const std::string & path,int * val)78 bool SysfsCollector::ReadFileToInt(const std::string &path, int *val) {
79     return ReadFileToInt(path.c_str(), val);
80 }
81 
ReadFileToInt(const char * const path,int * val)82 bool SysfsCollector::ReadFileToInt(const char *const path, int *val) {
83     std::string file_contents;
84 
85     if (!ReadFileToString(path, &file_contents)) {
86         ALOGE("Unable to read %s - %s", path, strerror(errno));
87         return false;
88     } else if (StartsWith(file_contents, "0x")) {
89         if (sscanf(file_contents.c_str(), "0x%x", val) != 1) {
90             ALOGE("Unable to convert %s to hex - %s", path, strerror(errno));
91             return false;
92         }
93     } else if (sscanf(file_contents.c_str(), "%d", val) != 1) {
94         ALOGE("Unable to convert %s to int - %s", path, strerror(errno));
95         return false;
96     }
97     return true;
98 }
99 
100 /**
101  * Read the contents of kCycleCountBinsPath and report them via IStats HAL.
102  * The contents are expected to be N buckets total, the nth of which indicates the
103  * number of times battery %-full has been increased with the n/N% full bucket.
104  */
logBatteryChargeCycles()105 void SysfsCollector::logBatteryChargeCycles() {
106     std::string file_contents;
107     int val;
108     std::vector<int> charge_cycles;
109     if (kCycleCountBinsPath == nullptr || strlen(kCycleCountBinsPath) == 0) {
110         ALOGV("Battery charge cycle path not specified");
111         return;
112     }
113     if (!ReadFileToString(kCycleCountBinsPath, &file_contents)) {
114         ALOGE("Unable to read battery charge cycles %s - %s", kCycleCountBinsPath, strerror(errno));
115         return;
116     }
117 
118     std::stringstream stream(file_contents);
119     while (stream >> val) {
120         charge_cycles.push_back(val);
121     }
122     ChargeCycles cycles;
123     cycles.cycleBucket = charge_cycles;
124 
125     std::replace(file_contents.begin(), file_contents.end(), ' ', ',');
126     stats_->reportChargeCycles(cycles);
127 }
128 
129 /**
130  * Read the contents of kEEPROMPath and report them.
131  */
logBatteryEEPROM()132 void SysfsCollector::logBatteryEEPROM() {
133     if (kEEPROMPath == nullptr || strlen(kEEPROMPath) == 0) {
134         ALOGV("Battery EEPROM path not specified");
135         return;
136     }
137 
138     battery_EEPROM_reporter_.checkAndReport(kEEPROMPath);
139 }
140 
141 /**
142  * Check the codec for failures over the past 24hr.
143  */
logCodecFailed()144 void SysfsCollector::logCodecFailed() {
145     std::string file_contents;
146     if (kCodecPath == nullptr || strlen(kCodecPath) == 0) {
147         ALOGV("Audio codec path not specified");
148         return;
149     }
150     if (!ReadFileToString(kCodecPath, &file_contents)) {
151         ALOGE("Unable to read codec state %s - %s", kCodecPath, strerror(errno));
152         return;
153     }
154     if (file_contents == "0") {
155         return;
156     } else {
157         HardwareFailed failed = {.hardwareType = HardwareFailed::HardwareType::CODEC,
158                                  .hardwareLocation = 0,
159                                  .errorCode = HardwareFailed::HardwareErrorCode::COMPLETE};
160         stats_->reportHardwareFailed(failed);
161     }
162 }
163 
164 /**
165  * Check the codec1 for failures over the past 24hr.
166  */
logCodec1Failed()167 void SysfsCollector::logCodec1Failed() {
168     std::string file_contents;
169     if (kCodec1Path == nullptr || strlen(kCodec1Path) == 0) {
170         ALOGV("Audio codec1 path not specified");
171         return;
172     }
173     if (!ReadFileToString(kCodec1Path, &file_contents)) {
174         ALOGE("Unable to read codec1 state %s - %s", kCodec1Path, strerror(errno));
175         return;
176     }
177     if (file_contents == "0") {
178         return;
179     } else {
180         ALOGE("%s report hardware fail", kCodec1Path);
181         HardwareFailed failed = {.hardwareType = HardwareFailed::HardwareType::CODEC,
182                                  .hardwareLocation = 1,
183                                  .errorCode = HardwareFailed::HardwareErrorCode::COMPLETE};
184         stats_->reportHardwareFailed(failed);
185     }
186 }
187 
reportSlowIoFromFile(const char * path,const SlowIo::IoOperation & operation_s)188 void SysfsCollector::reportSlowIoFromFile(const char *path,
189                                           const SlowIo::IoOperation &operation_s) {
190     std::string file_contents;
191     if (path == nullptr || strlen(path) == 0) {
192         ALOGV("slow_io path not specified");
193         return;
194     }
195     if (!ReadFileToString(path, &file_contents)) {
196         ALOGE("Unable to read slowio %s - %s", path, strerror(errno));
197         return;
198     } else {
199         int32_t slow_io_count = 0;
200         if (sscanf(file_contents.c_str(), "%d", &slow_io_count) != 1) {
201             ALOGE("Unable to parse %s from file %s to int.", file_contents.c_str(), path);
202         } else if (slow_io_count > 0) {
203             SlowIo slowio = {.operation = operation_s, .count = slow_io_count};
204             stats_->reportSlowIo(slowio);
205         }
206         // Clear the stats
207         if (!android::base::WriteStringToFile("0", path, true)) {
208             ALOGE("Unable to clear SlowIO entry %s - %s", path, strerror(errno));
209         }
210     }
211 }
212 
213 /**
214  * Check for slow IO operations.
215  */
logSlowIO()216 void SysfsCollector::logSlowIO() {
217     reportSlowIoFromFile(kSlowioReadCntPath, SlowIo::IoOperation::READ);
218     reportSlowIoFromFile(kSlowioWriteCntPath, SlowIo::IoOperation::WRITE);
219     reportSlowIoFromFile(kSlowioUnmapCntPath, SlowIo::IoOperation::UNMAP);
220     reportSlowIoFromFile(kSlowioSyncCntPath, SlowIo::IoOperation::SYNC);
221 }
222 
223 /**
224  * Report the last-detected impedance of left & right speakers.
225  */
logSpeakerImpedance()226 void SysfsCollector::logSpeakerImpedance() {
227     std::string file_contents;
228     if (kImpedancePath == nullptr || strlen(kImpedancePath) == 0) {
229         ALOGV("Audio impedance path not specified");
230         return;
231     }
232     if (!ReadFileToString(kImpedancePath, &file_contents)) {
233         ALOGE("Unable to read impedance path %s", kImpedancePath);
234         return;
235     }
236 
237     float left, right;
238     if (sscanf(file_contents.c_str(), "%g,%g", &left, &right) != 2) {
239         ALOGE("Unable to parse speaker impedance %s", file_contents.c_str());
240         return;
241     }
242     SpeakerImpedance left_obj = {.speakerLocation = 0,
243                                  .milliOhms = static_cast<int32_t>(left * 1000)};
244     SpeakerImpedance right_obj = {.speakerLocation = 1,
245                                   .milliOhms = static_cast<int32_t>(right * 1000)};
246     stats_->reportSpeakerImpedance(left_obj);
247     stats_->reportSpeakerImpedance(right_obj);
248 }
249 
250 /**
251  * Report the Speech DSP state.
252  */
logSpeechDspStat()253 void SysfsCollector::logSpeechDspStat() {
254     std::string file_contents;
255     if (kSpeechDspPath == nullptr || strlen(kSpeechDspPath) == 0) {
256         ALOGV("Speech DSP path not specified");
257         return;
258     }
259     if (!ReadFileToString(kSpeechDspPath, &file_contents)) {
260         ALOGE("Unable to read speech dsp path %s", kSpeechDspPath);
261         return;
262     }
263 
264     int32_t uptime = 0, downtime = 0, crashcount = 0, recovercount = 0;
265     if (sscanf(file_contents.c_str(), "%d,%d,%d,%d", &uptime, &downtime, &crashcount,
266                &recovercount) != 4) {
267         ALOGE("Unable to parse speech dsp stat %s", file_contents.c_str());
268         return;
269     }
270 
271     ALOGD("SpeechDSP uptime %d downtime %d crashcount %d recovercount %d", uptime, downtime,
272           crashcount, recovercount);
273     SpeechDspStat dspstat = {.totalUptimeMillis = uptime,
274                              .totalDowntimeMillis = downtime,
275                              .totalCrashCount = crashcount,
276                              .totalRecoverCount = recovercount};
277 
278     stats_->reportSpeechDspStat(dspstat);
279 }
280 
logBatteryCapacity()281 void SysfsCollector::logBatteryCapacity() {
282     std::string file_contents;
283     if (kBatteryCapacityCC == nullptr || strlen(kBatteryCapacityCC) == 0) {
284         ALOGV("Battery Capacity CC path not specified");
285         return;
286     }
287     if (kBatteryCapacityVFSOC == nullptr || strlen(kBatteryCapacityVFSOC) == 0) {
288         ALOGV("Battery Capacity VFSOC path not specified");
289         return;
290     }
291     int delta_cc_sum, delta_vfsoc_sum;
292     if (!ReadFileToInt(kBatteryCapacityCC, &delta_cc_sum) ||
293 	!ReadFileToInt(kBatteryCapacityVFSOC, &delta_vfsoc_sum))
294 	return;
295 
296     // Load values array
297     std::vector<VendorAtom::Value> values(2);
298     VendorAtom::Value tmp;
299     tmp.intValue(delta_cc_sum);
300     values[BatteryCapacity::kDeltaCcSumFieldNumber - kVendorAtomOffset] = tmp;
301     tmp.intValue(delta_vfsoc_sum);
302     values[BatteryCapacity::kDeltaVfsocSumFieldNumber - kVendorAtomOffset] = tmp;
303 
304     // Send vendor atom to IStats HAL
305     VendorAtom event = {.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
306                         .atomId = PixelAtoms::Ids::BATTERY_CAPACITY,
307                         .values = values};
308     Return<void> ret = stats_->reportVendorAtom(event);
309     if (!ret.isOk())
310         ALOGE("Unable to report ChargeStats to Stats service");
311 }
312 
logUFSLifetime()313 void SysfsCollector::logUFSLifetime() {
314     std::string file_contents;
315     if (kUFSLifetimeA == nullptr || strlen(kUFSLifetimeA) == 0) {
316         ALOGV("UFS lifetimeA path not specified");
317         return;
318     }
319     if (kUFSLifetimeB == nullptr || strlen(kUFSLifetimeB) == 0) {
320         ALOGV("UFS lifetimeB path not specified");
321         return;
322     }
323     if (kUFSLifetimeC == nullptr || strlen(kUFSLifetimeC) == 0) {
324         ALOGV("UFS lifetimeC path not specified");
325         return;
326     }
327 
328     int lifetimeA = 0, lifetimeB = 0, lifetimeC = 0;
329     if (!ReadFileToInt(kUFSLifetimeA, &lifetimeA) ||
330         !ReadFileToInt(kUFSLifetimeB, &lifetimeB) ||
331         !ReadFileToInt(kUFSLifetimeC, &lifetimeC)) {
332         ALOGE("Unable to read UFS lifetime : %s", strerror(errno));
333         return;
334     }
335 
336     // Load values array
337     std::vector<VendorAtom::Value> values(3);
338     VendorAtom::Value tmp;
339     tmp.intValue(lifetimeA);
340     values[StorageUfsHealth::kLifetimeAFieldNumber - kVendorAtomOffset] = tmp;
341     tmp.intValue(lifetimeB);
342     values[StorageUfsHealth::kLifetimeBFieldNumber - kVendorAtomOffset] = tmp;
343     tmp.intValue(lifetimeC);
344     values[StorageUfsHealth::kLifetimeCFieldNumber - kVendorAtomOffset] = tmp;
345 
346 
347     // Send vendor atom to IStats HAL
348     VendorAtom event = {.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
349                         .atomId = PixelAtoms::Ids::STORAGE_UFS_HEALTH,
350                         .values = values};
351     Return<void> ret = stats_->reportVendorAtom(event);
352     if (!ret.isOk()) {
353         ALOGE("Unable to report UfsHealthStat to Stats service");
354     }
355 }
356 
getUserDataBlock()357 static std::string getUserDataBlock() {
358     std::unique_ptr<std::FILE, int (*)(std::FILE*)> fp(setmntent("/proc/mounts", "re"), endmntent);
359     if (fp == nullptr) {
360         ALOGE("Error opening /proc/mounts");
361         return "";
362     }
363 
364     mntent* mentry;
365     while ((mentry = getmntent(fp.get())) != nullptr) {
366         if (strcmp(mentry->mnt_dir, "/data") == 0) {
367             return std::string(basename(mentry->mnt_fsname));
368         }
369     }
370     return "";
371 }
372 
logF2fsStats()373 void SysfsCollector::logF2fsStats() {
374     int dirty, free, cp_calls_fg, gc_calls_fg, moved_block_fg, vblocks;
375     int cp_calls_bg, gc_calls_bg, moved_block_bg;
376 
377     if (kF2fsStatsPath == nullptr) {
378         ALOGE("F2fs stats path not specified");
379         return;
380     }
381 
382     std::string userdataBlock = getUserDataBlock();
383 
384     if (!ReadFileToInt(kF2fsStatsPath + (userdataBlock + "/dirty_segments"), &dirty)) {
385         ALOGV("Unable to read dirty segments");
386     }
387 
388     if (!ReadFileToInt(kF2fsStatsPath + (userdataBlock + "/free_segments"), &free)) {
389         ALOGV("Unable to read free segments");
390     }
391 
392     if (!ReadFileToInt(kF2fsStatsPath + (userdataBlock + "/cp_foreground_calls"), &cp_calls_fg)) {
393         ALOGV("Unable to read cp_foreground_calls");
394     }
395 
396     if (!ReadFileToInt(kF2fsStatsPath + (userdataBlock + "/cp_background_calls"), &cp_calls_bg)) {
397         ALOGV("Unable to read cp_background_calls");
398     }
399 
400     if (!ReadFileToInt(kF2fsStatsPath + (userdataBlock + "/gc_foreground_calls"), &gc_calls_fg)) {
401         ALOGV("Unable to read gc_foreground_calls");
402     }
403 
404     if (!ReadFileToInt(kF2fsStatsPath + (userdataBlock + "/gc_background_calls"), &gc_calls_bg)) {
405         ALOGV("Unable to read gc_background_calls");
406     }
407 
408     if (!ReadFileToInt(kF2fsStatsPath + (userdataBlock + "/moved_blocks_foreground"), &moved_block_fg)) {
409         ALOGV("Unable to read moved_blocks_foreground");
410     }
411 
412     if (!ReadFileToInt(kF2fsStatsPath + (userdataBlock + "/moved_blocks_background"), &moved_block_bg)) {
413         ALOGV("Unable to read moved_blocks_background");
414     }
415 
416     if (!ReadFileToInt(kF2fsStatsPath + (userdataBlock + "/avg_vblocks"), &vblocks)) {
417         ALOGV("Unable to read avg_vblocks");
418     }
419 
420     // Load values array
421     std::vector<VendorAtom::Value> values(9);
422     VendorAtom::Value tmp;
423     tmp.intValue(dirty);
424     values[F2fsStatsInfo::kDirtySegmentsFieldNumber - kVendorAtomOffset] = tmp;
425     tmp.intValue(free);
426     values[F2fsStatsInfo::kFreeSegmentsFieldNumber - kVendorAtomOffset] = tmp;
427     tmp.intValue(cp_calls_fg);
428     values[F2fsStatsInfo::kCpCallsFgFieldNumber - kVendorAtomOffset] = tmp;
429     tmp.intValue(cp_calls_bg);
430     values[F2fsStatsInfo::kCpCallsBgFieldNumber - kVendorAtomOffset] = tmp;
431     tmp.intValue(gc_calls_fg);
432     values[F2fsStatsInfo::kGcCallsFgFieldNumber - kVendorAtomOffset] = tmp;
433     tmp.intValue(gc_calls_bg);
434     values[F2fsStatsInfo::kGcCallsBgFieldNumber - kVendorAtomOffset] = tmp;
435     tmp.intValue(moved_block_fg);
436     values[F2fsStatsInfo::kMovedBlocksFgFieldNumber - kVendorAtomOffset] = tmp;
437     tmp.intValue(moved_block_bg);
438     values[F2fsStatsInfo::kMovedBlocksBgFieldNumber - kVendorAtomOffset] = tmp;
439     tmp.intValue(vblocks);
440     values[F2fsStatsInfo::kValidBlocksFieldNumber - kVendorAtomOffset] = tmp;
441 
442     // Send vendor atom to IStats HAL
443     VendorAtom event = {.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
444                         .atomId = PixelAtoms::Ids::F2FS_STATS,
445                         .values = values};
446     Return<void> ret = stats_->reportVendorAtom(event);
447     if (!ret.isOk()) {
448         ALOGE("Unable to report F2fs stats to Stats service");
449     }
450 }
451 
reportZramMmStat()452 void SysfsCollector::reportZramMmStat() {
453     std::string file_contents;
454     if (!kZramMmStatPath) {
455         ALOGV("ZramMmStat path not specified");
456         return;
457     }
458 
459     if (!ReadFileToString(kZramMmStatPath, &file_contents)) {
460         ALOGE("Unable to ZramMmStat %s - %s", kZramMmStatPath, strerror(errno));
461         return;
462     } else {
463         int64_t orig_data_size = 0;
464         int64_t compr_data_size = 0;
465         int64_t mem_used_total = 0;
466         int64_t mem_limit = 0;
467         int64_t max_used_total = 0;
468         int64_t same_pages = 0;
469         int64_t pages_compacted = 0;
470         int64_t huge_pages = 0;
471 
472         if (sscanf(file_contents.c_str(), "%lu %lu %lu %lu %lu %lu %lu %lu",
473                     &orig_data_size, &compr_data_size, &mem_used_total, &mem_limit,
474                     &max_used_total, &same_pages, &pages_compacted, &huge_pages) != 8) {
475             ALOGE("Unable to parse ZramMmStat %s from file %s to int.",
476                     file_contents.c_str(), kZramMmStatPath);
477         }
478 
479         // Load values array
480         std::vector<VendorAtom::Value> values(5);
481         VendorAtom::Value tmp;
482         tmp.intValue(orig_data_size);
483         values[ZramMmStat::kOrigDataSizeFieldNumber - kVendorAtomOffset] = tmp;
484         tmp.intValue(compr_data_size);
485         values[ZramMmStat::kComprDataSizeFieldNumber - kVendorAtomOffset] = tmp;
486         tmp.intValue(mem_used_total);
487         values[ZramMmStat::kMemUsedTotalFieldNumber - kVendorAtomOffset] = tmp;
488         tmp.intValue(same_pages);
489         values[ZramMmStat::kSamePagesFieldNumber - kVendorAtomOffset] = tmp;
490         tmp.intValue(huge_pages);
491         values[ZramMmStat::kHugePagesFieldNumber - kVendorAtomOffset] = tmp;
492 
493         // Send vendor atom to IStats HAL
494         VendorAtom event = {.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
495             .atomId = PixelAtoms::Ids::ZRAM_MM_STAT,
496             .values = values};
497         Return<void> ret = stats_->reportVendorAtom(event);
498         if (!ret.isOk())
499             ALOGE("Zram Unable to report ZramMmStat to Stats service");
500     }
501 }
502 
reportZramBdStat()503 void SysfsCollector::reportZramBdStat() {
504     std::string file_contents;
505     if (!kZramBdStatPath) {
506         ALOGV("ZramBdStat path not specified");
507         return;
508     }
509 
510     if (!ReadFileToString(kZramBdStatPath, &file_contents)) {
511         ALOGE("Unable to ZramBdStat %s - %s", kZramBdStatPath, strerror(errno));
512         return;
513     } else {
514         int64_t bd_count = 0;
515         int64_t bd_reads = 0;
516         int64_t bd_writes = 0;
517 
518         if (sscanf(file_contents.c_str(), "%lu %lu %lu",
519                                 &bd_count, &bd_reads, &bd_writes) != 3) {
520             ALOGE("Unable to parse ZramBdStat %s from file %s to int.",
521                     file_contents.c_str(), kZramBdStatPath);
522         }
523 
524         // Load values array
525         std::vector<VendorAtom::Value> values(3);
526         VendorAtom::Value tmp;
527         tmp.intValue(bd_count);
528         values[ZramBdStat::kBdCountFieldNumber - kVendorAtomOffset] = tmp;
529         tmp.intValue(bd_reads);
530         values[ZramBdStat::kBdReadsFieldNumber - kVendorAtomOffset] = tmp;
531         tmp.intValue(bd_writes);
532         values[ZramBdStat::kBdWritesFieldNumber - kVendorAtomOffset] = tmp;
533 
534         // Send vendor atom to IStats HAL
535         VendorAtom event = {.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
536             .atomId = PixelAtoms::Ids::ZRAM_BD_STAT,
537             .values = values};
538         Return<void> ret = stats_->reportVendorAtom(event);
539         if (!ret.isOk())
540             ALOGE("Zram Unable to report ZramBdStat to Stats service");
541     }
542 }
543 
logZramStats()544 void SysfsCollector::logZramStats() {
545     reportZramMmStat();
546     reportZramBdStat();
547 }
548 
logBootStats()549 void SysfsCollector::logBootStats() {
550     int mounted_time_sec = 0;
551 
552     if (kF2fsStatsPath == nullptr) {
553         ALOGE("F2fs stats path not specified");
554         return;
555     }
556 
557     std::string userdataBlock = getUserDataBlock();
558 
559     if (!ReadFileToInt(kF2fsStatsPath + (userdataBlock + "/mounted_time_sec"), &mounted_time_sec)) {
560         ALOGV("Unable to read mounted_time_sec");
561         return;
562     }
563 
564     int fsck_time_ms = android::base::GetIntProperty("ro.boottime.init.fsck.data", 0);
565     int checkpoint_time_ms = android::base::GetIntProperty("ro.boottime.init.mount.data", 0);
566 
567     if (fsck_time_ms == 0 && checkpoint_time_ms == 0) {
568         ALOGV("Not yet initialized");
569         return;
570     }
571 
572     // Load values array
573     std::vector<VendorAtom::Value> values(3);
574     VendorAtom::Value tmp;
575     tmp.intValue(mounted_time_sec);
576     values[BootStatsInfo::kMountedTimeSecFieldNumber - kVendorAtomOffset] = tmp;
577     tmp.intValue(fsck_time_ms / 1000);
578     values[BootStatsInfo::kFsckTimeSecFieldNumber - kVendorAtomOffset] = tmp;
579     tmp.intValue(checkpoint_time_ms / 1000);
580     values[BootStatsInfo::kCheckpointTimeSecFieldNumber - kVendorAtomOffset] = tmp;
581 
582     // Send vendor atom to IStats HAL
583     VendorAtom event = {.reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
584                         .atomId = PixelAtoms::Ids::BOOT_STATS,
585                         .values = values};
586     Return<void> ret = stats_->reportVendorAtom(event);
587     if (!ret.isOk()) {
588         ALOGE("Unable to report Boot stats to Stats service");
589     } else {
590         log_once_reported = true;
591     }
592 }
593 
logAll()594 void SysfsCollector::logAll() {
595     stats_ = IStats::tryGetService();
596     if (!stats_) {
597         ALOGE("Unable to connect to Stats service");
598         return;
599     }
600 
601     // Collect once per service init; can be multiple due to service reinit
602     if (!log_once_reported) {
603         logBootStats();
604     }
605     logBatteryChargeCycles();
606     logCodecFailed();
607     logCodec1Failed();
608     logSlowIO();
609     logSpeakerImpedance();
610     logSpeechDspStat();
611     logBatteryCapacity();
612     logUFSLifetime();
613     logF2fsStats();
614     logZramStats();
615     logBatteryEEPROM();
616 
617     stats_.clear();
618 }
619 
620 /**
621  * Loop forever collecting stats from sysfs nodes and reporting them via
622  * IStats.
623  */
collect(void)624 void SysfsCollector::collect(void) {
625     int timerfd = timerfd_create(CLOCK_BOOTTIME, 0);
626     if (timerfd < 0) {
627         ALOGE("Unable to create timerfd - %s", strerror(errno));
628         return;
629     }
630 
631     // Sleep for 30 seconds on launch to allow codec driver to load.
632     sleep(30);
633 
634     // Collect first set of stats on boot.
635     logAll();
636 
637     // Collect stats every 24hrs after.
638     struct itimerspec period;
639     const int kSecondsPerDay = 60 * 60 * 24;
640     period.it_interval.tv_sec = kSecondsPerDay;
641     period.it_interval.tv_nsec = 0;
642     period.it_value.tv_sec = kSecondsPerDay;
643     period.it_value.tv_nsec = 0;
644 
645     if (timerfd_settime(timerfd, 0, &period, NULL)) {
646         ALOGE("Unable to set 24hr timer - %s", strerror(errno));
647         return;
648     }
649 
650     while (1) {
651         int readval;
652         do {
653             char buf[8];
654             errno = 0;
655             readval = read(timerfd, buf, sizeof(buf));
656         } while (readval < 0 && errno == EINTR);
657         if (readval < 0) {
658             ALOGE("Timerfd error - %s\n", strerror(errno));
659             return;
660         }
661         logAll();
662     }
663 }
664 
665 }  // namespace pixel
666 }  // namespace google
667 }  // namespace hardware
668 }  // namespace android
669