• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 /* If you are watching for a new uevent, uncomment the following define.
18  * After flashing your test build, run:
19  *    adb root && adb shell
20  *    stop vendor.pixelstats_vendor
21  *    touch /data/local/tmp/uevents
22  *    /vendor/bin/pixelstats-vendor &
23  *
24  *    then trigger any events.
25  *    If you leave adb connected, you can watch them with
26  *    tail -f /data/local/tmp/uevents
27  *
28  *    Once you are done,
29  *
30  *    adb pull /data/local/tmp/uevents
31  *    adb rm /data/local/tmp/uevents
32  *    adb reboot
33  *
34  *    provide this log in the bug as support for your feature.
35  */
36 // #define LOG_UEVENTS_TO_FILE_ONLY_FOR_DEVEL "/data/local/tmp/uevents"
37 
38 #define LOG_TAG "pixelstats-uevent"
39 
40 #include <android-base/file.h>
41 #include <android-base/logging.h>
42 #include <android-base/parseint.h>
43 #include <android-base/strings.h>
44 #include <android/binder_manager.h>
45 #include <cutils/uevent.h>
46 #include <fcntl.h>
47 #include <hardware/google/pixel/pixelstats/pixelatoms.pb.h>
48 #include <log/log.h>
49 #include <pixelstats/StatsHelper.h>
50 #include <pixelstats/UeventListener.h>
51 #include <sys/stat.h>
52 #include <sys/types.h>
53 #include <unistd.h>
54 #include <utils/StrongPointer.h>
55 
56 #include <string>
57 #include <thread>
58 
59 namespace android {
60 namespace hardware {
61 namespace google {
62 namespace pixel {
63 
64 using aidl::android::frameworks::stats::VendorAtom;
65 using aidl::android::frameworks::stats::VendorAtomValue;
66 using android::sp;
67 using android::base::ReadFileToString;
68 using android::base::WriteStringToFile;
69 using android::hardware::google::pixel::PixelAtoms::GpuEvent;
70 using android::hardware::google::pixel::PixelAtoms::PdVidPid;
71 using android::hardware::google::pixel::PixelAtoms::VendorHardwareFailed;
72 using android::hardware::google::pixel::PixelAtoms::VendorUsbPortOverheat;
73 
74 constexpr int32_t UEVENT_MSG_LEN = 2048;  // it's 2048 in all other users.
75 constexpr int32_t PRODUCT_TYPE_OFFSET = 23;
76 constexpr int32_t PRODUCT_TYPE_MASK = 7;
77 constexpr int32_t PRODUCT_TYPE_CHARGER = 3;
78 constexpr int32_t VID_MASK = 0xffff;
79 constexpr int32_t VID_GOOGLE = 0x18d1;
80 constexpr int32_t PID_OFFSET = 2;
81 constexpr int32_t PID_LENGTH = 4;
82 constexpr uint32_t PID_P30 = 0x4f05;
83 
ReadFileToInt(const std::string & path,int * val)84 bool UeventListener::ReadFileToInt(const std::string &path, int *val) {
85     return ReadFileToInt(path.c_str(), val);
86 }
87 
ReadFileToInt(const char * const path,int * val)88 bool UeventListener::ReadFileToInt(const char *const path, int *val) {
89     std::string file_contents;
90 
91     if (!ReadFileToString(path, &file_contents)) {
92         ALOGE("Unable to read %s - %s", path, strerror(errno));
93         return false;
94     } else if (sscanf(file_contents.c_str(), "%d", val) != 1) {
95         ALOGE("Unable to convert %s to int - %s", path, strerror(errno));
96         return false;
97     }
98     return true;
99 }
100 
ReportMicBrokenOrDegraded(const std::shared_ptr<IStats> & stats_client,const int mic,const bool isbroken)101 void UeventListener::ReportMicBrokenOrDegraded(const std::shared_ptr<IStats> &stats_client,
102                                                const int mic, const bool isbroken) {
103     VendorHardwareFailed failure;
104     failure.set_hardware_type(VendorHardwareFailed::HARDWARE_FAILED_MICROPHONE);
105     failure.set_hardware_location(mic);
106     failure.set_failure_code(isbroken ? VendorHardwareFailed::COMPLETE
107                                       : VendorHardwareFailed::DEGRADE);
108     reportHardwareFailed(stats_client, failure);
109 }
110 
ReportMicStatusUevents(const std::shared_ptr<IStats> & stats_client,const char * devpath,const char * mic_status)111 void UeventListener::ReportMicStatusUevents(const std::shared_ptr<IStats> &stats_client,
112                                             const char *devpath, const char *mic_status) {
113     if (!devpath || !mic_status)
114         return;
115     if (!strcmp(devpath, ("DEVPATH=" + kAudioUevent).c_str())) {
116         std::vector<std::string> value = android::base::Split(mic_status, "=");
117         bool isbroken;
118 
119         if (value.size() == 2) {
120             if (!value[0].compare("MIC_BREAK_STATUS"))
121                 isbroken = true;
122             else if (!value[0].compare("MIC_DEGRADE_STATUS"))
123                 isbroken = false;
124             else
125                 return;
126 
127             if (!value[1].compare("true")) {
128                 ReportMicBrokenOrDegraded(stats_client, 0, isbroken);
129             } else {
130                 int mic_status = atoi(value[1].c_str());
131 
132                 if (mic_status > 0 && mic_status <= 7) {
133                     for (int mic_bit = 0; mic_bit < 3; mic_bit++)
134                         if (mic_status & (0x1 << mic_bit))
135                             ReportMicBrokenOrDegraded(stats_client, mic_bit, isbroken);
136                 } else if (mic_status == 0) {
137                     // mic is ok
138                     return;
139                 } else {
140                     // should not enter here
141                     ALOGE("invalid mic status");
142                     return;
143                 }
144             }
145         }
146     }
147 }
148 
ReportUsbPortOverheatEvent(const std::shared_ptr<IStats> & stats_client,const char * driver)149 void UeventListener::ReportUsbPortOverheatEvent(const std::shared_ptr<IStats> &stats_client,
150                                                 const char *driver) {
151     if (!driver || strcmp(driver, "DRIVER=google,overheat_mitigation")) {
152         return;
153     }
154 
155     int32_t plug_temperature_deci_c = 0;
156     int32_t max_temperature_deci_c = 0;
157     int32_t time_to_overheat_secs = 0;
158     int32_t time_to_hysteresis_secs = 0;
159     int32_t time_to_inactive_secs = 0;
160 
161     // TODO(achant b/182941868): test return value and skip reporting in case of an error
162     ReadFileToInt((kUsbPortOverheatPath + "/plug_temp"), &plug_temperature_deci_c);
163     ReadFileToInt((kUsbPortOverheatPath + "/max_temp"), &max_temperature_deci_c);
164     ReadFileToInt((kUsbPortOverheatPath + "/trip_time"), &time_to_overheat_secs);
165     ReadFileToInt((kUsbPortOverheatPath + "/hysteresis_time"), &time_to_hysteresis_secs);
166     ReadFileToInt((kUsbPortOverheatPath + "/cleared_time"), &time_to_inactive_secs);
167 
168     VendorUsbPortOverheat overheat_info;
169     overheat_info.set_plug_temperature_deci_c(plug_temperature_deci_c);
170     overheat_info.set_max_temperature_deci_c(max_temperature_deci_c);
171     overheat_info.set_time_to_overheat_secs(time_to_overheat_secs);
172     overheat_info.set_time_to_hysteresis_secs(time_to_hysteresis_secs);
173     overheat_info.set_time_to_inactive_secs(time_to_inactive_secs);
174 
175     reportUsbPortOverheat(stats_client, overheat_info);
176 }
177 
ReportChargeMetricsEvent(const std::shared_ptr<IStats> & stats_client,const char * driver)178 void UeventListener::ReportChargeMetricsEvent(const std::shared_ptr<IStats> &stats_client,
179                                               const char *driver) {
180     if (!driver || strcmp(driver, "DRIVER=google,battery")) {
181         return;
182     }
183 
184     charge_stats_reporter_.checkAndReport(stats_client, kChargeMetricsPath);
185 }
186 
187 /**
188  * Report raw battery capacity, system battery capacity and associated
189  * battery capacity curves. This data is collected to verify the filter
190  * applied on the battery capacity. This will allow debugging of issues
191  * ranging from incorrect fuel gauge hardware calculations to issues
192  * with the software reported battery capacity.
193  *
194  * The data is retrieved by parsing the battery power supply's ssoc_details.
195  *
196  * This atom logs data in 5 potential events:
197  *      1. When a device is connected
198  *      2. When a device is disconnected
199  *      3. When a device has reached a full charge (from the UI's perspective)
200  *      4. When there is a >= 2 percent skip in the UI reported SOC
201  *      5. When there is a difference of >= 4 percent between the raw hardware
202  *          battery capacity and the system reported battery capacity.
203  */
ReportBatteryCapacityFGEvent(const std::shared_ptr<IStats> & stats_client,const char * subsystem)204 void UeventListener::ReportBatteryCapacityFGEvent(const std::shared_ptr<IStats> &stats_client,
205                                                   const char *subsystem) {
206     if (!subsystem || strcmp(subsystem, "SUBSYSTEM=power_supply")) {
207         return;
208     }
209 
210     // Indicates an implicit disable of the battery capacity reporting
211     if (kBatterySSOCPath.empty()) {
212         return;
213     }
214 
215     battery_capacity_reporter_.checkAndReport(stats_client, kBatterySSOCPath);
216 }
217 
ReportTypeCPartnerId(const std::shared_ptr<IStats> & stats_client)218 void UeventListener::ReportTypeCPartnerId(const std::shared_ptr<IStats> &stats_client) {
219     std::string file_contents_vid, file_contents_pid;
220     uint32_t pid, vid;
221 
222     if (!ReadFileToString(kTypeCPartnerVidPath.c_str(), &file_contents_vid)) {
223         ALOGE("Unable to read %s - %s", kTypeCPartnerVidPath.c_str(), strerror(errno));
224         return;
225     }
226 
227     if (sscanf(file_contents_vid.c_str(), "%x", &vid) != 1) {
228         ALOGE("Unable to parse vid %s from file %s to int.", file_contents_vid.c_str(),
229               kTypeCPartnerVidPath.c_str());
230         return;
231     }
232 
233     if (!ReadFileToString(kTypeCPartnerPidPath.c_str(), &file_contents_pid)) {
234         ALOGE("Unable to read %s - %s", kTypeCPartnerPidPath.c_str(), strerror(errno));
235         return;
236     }
237 
238     if (sscanf(file_contents_pid.substr(PID_OFFSET, PID_LENGTH).c_str(), "%x", &pid) != 1) {
239         ALOGE("Unable to parse pid %s from file %s to int.",
240               file_contents_pid.substr(PID_OFFSET, PID_LENGTH).c_str(),
241               kTypeCPartnerPidPath.c_str());
242         return;
243     }
244 
245     // Upload data only for Google VID
246     if ((VID_MASK & vid) != VID_GOOGLE) {
247         return;
248     }
249 
250     // Upload data only for chargers unless for P30 PID where the product type
251     // isn't set to charger.
252     if ((((vid >> PRODUCT_TYPE_OFFSET) & PRODUCT_TYPE_MASK) != PRODUCT_TYPE_CHARGER) &&
253         (pid != PID_P30)) {
254         return;
255     }
256 
257     std::vector<VendorAtomValue> values(2);
258     VendorAtomValue tmp;
259 
260     tmp.set<VendorAtomValue::intValue>(vid & VID_MASK);
261     values[PdVidPid::kVidFieldNumber - kVendorAtomOffset] = tmp;
262     tmp.set<VendorAtomValue::intValue>(pid);
263     values[PdVidPid::kPidFieldNumber - kVendorAtomOffset] = tmp;
264 
265     // Send vendor atom to IStats HAL
266     VendorAtom event = {.reverseDomainName = "",
267                         .atomId = PixelAtoms::Atom::kPdVidPid,
268                         .values = std::move(values)};
269     const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
270     if (!ret.isOk()) {
271         ALOGE("Unable to report PD VID/PID to Stats service");
272     }
273 }
274 
ReportGpuEvent(const std::shared_ptr<IStats> & stats_client,const char * driver,const char * gpu_event_type,const char * gpu_event_info)275 void UeventListener::ReportGpuEvent(const std::shared_ptr<IStats> &stats_client, const char *driver,
276                                     const char *gpu_event_type, const char *gpu_event_info) {
277     if (!stats_client || !driver || strncmp(driver, "DRIVER=mali", strlen("DRIVER=mali")) ||
278         !gpu_event_type || !gpu_event_info)
279         return;
280 
281     std::vector<std::string> type = android::base::Split(gpu_event_type, "=");
282     std::vector<std::string> info = android::base::Split(gpu_event_info, "=");
283 
284     if (type.size() != 2 || info.size() != 2)
285         return;
286 
287     if (type[0] != "GPU_UEVENT_TYPE" || info[0] != "GPU_UEVENT_INFO")
288         return;
289 
290     auto event_type = kGpuEventTypeStrToEnum.find(type[1]);
291     auto event_info = kGpuEventInfoStrToEnum.find(info[1]);
292     if (event_type == kGpuEventTypeStrToEnum.end() || event_info == kGpuEventInfoStrToEnum.end())
293         return;
294 
295     VendorAtom event = {.reverseDomainName = "",
296                         .atomId = PixelAtoms::Atom::kGpuEvent,
297                         .values = {event_type->second, event_info->second}};
298     const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
299     if (!ret.isOk())
300         ALOGE("Unable to report GPU event.");
301 }
302 
ProcessUevent()303 bool UeventListener::ProcessUevent() {
304     char msg[UEVENT_MSG_LEN + 2];
305     char *cp;
306     const char *driver, *product, *subsystem;
307     const char *mic_break_status, *mic_degrade_status;
308     const char *devpath;
309     bool collect_partner_id = false;
310     const char *gpu_event_type = nullptr, *gpu_event_info = nullptr;
311     int n;
312 
313     if (uevent_fd_ < 0) {
314         uevent_fd_ = uevent_open_socket(64 * 1024, true);
315         if (uevent_fd_ < 0) {
316             ALOGE("uevent_init: uevent_open_socket failed\n");
317             return false;
318         }
319     }
320 
321 #ifdef LOG_UEVENTS_TO_FILE_ONLY_FOR_DEVEL
322     if (log_fd_ < 0) {
323         /* Intentionally no O_CREAT so no logging will happen
324          * unless the user intentionally 'touch's the file.
325          */
326         log_fd_ = open(LOG_UEVENTS_TO_FILE_ONLY_FOR_DEVEL, O_WRONLY);
327     }
328 #endif
329 
330     n = uevent_kernel_multicast_recv(uevent_fd_, msg, UEVENT_MSG_LEN);
331     if (n <= 0 || n >= UEVENT_MSG_LEN)
332         return false;
333 
334     // Ensure double-null termination of msg.
335     msg[n] = '\0';
336     msg[n + 1] = '\0';
337 
338     driver = product = subsystem = NULL;
339     mic_break_status = mic_degrade_status = devpath = NULL;
340 
341     /**
342      * msg is a sequence of null-terminated strings.
343      * Iterate through and record positions of string/value pairs of interest.
344      * Double null indicates end of the message. (enforced above).
345      */
346     cp = msg;
347     while (*cp) {
348         if (log_fd_ > 0) {
349             write(log_fd_, cp, strlen(cp));
350             write(log_fd_, "\n", 1);
351         }
352         if (!strncmp(cp, "DRIVER=", strlen("DRIVER="))) {
353             driver = cp;
354         } else if (!strncmp(cp, "PRODUCT=", strlen("PRODUCT="))) {
355             product = cp;
356         } else if (!strncmp(cp, "MIC_BREAK_STATUS=", strlen("MIC_BREAK_STATUS="))) {
357             mic_break_status = cp;
358         } else if (!strncmp(cp, "MIC_DEGRADE_STATUS=", strlen("MIC_DEGRADE_STATUS="))) {
359             mic_degrade_status = cp;
360         } else if (!strncmp(cp, "DEVPATH=", strlen("DEVPATH="))) {
361             devpath = cp;
362         } else if (!strncmp(cp, "SUBSYSTEM=", strlen("SUBSYSTEM="))) {
363             subsystem = cp;
364         } else if (!strncmp(cp, kTypeCPartnerUevent.c_str(), kTypeCPartnerUevent.size())) {
365             collect_partner_id = true;
366         } else if (!strncmp(cp, "GPU_UEVENT_TYPE=", strlen("GPU_UEVENT_TYPE="))) {
367             gpu_event_type = cp;
368         } else if (!strncmp(cp, "GPU_UEVENT_INFO=", strlen("GPU_UEVENT_INFO="))) {
369             gpu_event_info = cp;
370         }
371         /* advance to after the next \0 */
372         while (*cp++) {
373         }
374     }
375 
376     std::shared_ptr<IStats> stats_client = getStatsService();
377     if (!stats_client) {
378         ALOGE("Unable to get Stats service instance.");
379     } else {
380         /* Process the strings recorded. */
381         ReportMicStatusUevents(stats_client, devpath, mic_break_status);
382         ReportMicStatusUevents(stats_client, devpath, mic_degrade_status);
383         ReportUsbPortOverheatEvent(stats_client, driver);
384         ReportChargeMetricsEvent(stats_client, driver);
385         ReportBatteryCapacityFGEvent(stats_client, subsystem);
386         if (collect_partner_id) {
387             ReportTypeCPartnerId(stats_client);
388         }
389         ReportGpuEvent(stats_client, driver, gpu_event_type, gpu_event_info);
390     }
391 
392     if (log_fd_ > 0) {
393         write(log_fd_, "\n", 1);
394     }
395     return true;
396 }
397 
UeventListener(const std::string audio_uevent,const std::string ssoc_details_path,const std::string overheat_path,const std::string charge_metrics_path,const std::string typec_partner_vid_path,const std::string typec_partner_pid_path)398 UeventListener::UeventListener(const std::string audio_uevent, const std::string ssoc_details_path,
399                                const std::string overheat_path,
400                                const std::string charge_metrics_path,
401                                const std::string typec_partner_vid_path,
402                                const std::string typec_partner_pid_path)
403     : kAudioUevent(audio_uevent),
404       kBatterySSOCPath(ssoc_details_path),
405       kUsbPortOverheatPath(overheat_path),
406       kChargeMetricsPath(charge_metrics_path),
407       kTypeCPartnerUevent(typec_partner_uevent_default),
408       kTypeCPartnerVidPath(typec_partner_vid_path),
409       kTypeCPartnerPidPath(typec_partner_pid_path),
410       uevent_fd_(-1),
411       log_fd_(-1) {}
412 
UeventListener(const struct UeventPaths & uevents_paths)413 UeventListener::UeventListener(const struct UeventPaths &uevents_paths)
414     : kAudioUevent((uevents_paths.AudioUevent == nullptr) ? "" : uevents_paths.AudioUevent),
415       kBatterySSOCPath((uevents_paths.SsocDetailsPath == nullptr) ? ssoc_details_path
416                                                                   : uevents_paths.SsocDetailsPath),
417       kUsbPortOverheatPath((uevents_paths.OverheatPath == nullptr) ? overheat_path_default
418                                                                    : uevents_paths.OverheatPath),
419       kChargeMetricsPath((uevents_paths.ChargeMetricsPath == nullptr)
420                                  ? charge_metrics_path_default
421                                  : uevents_paths.ChargeMetricsPath),
422       kTypeCPartnerUevent((uevents_paths.TypeCPartnerUevent == nullptr)
423                                   ? typec_partner_uevent_default
424                                   : uevents_paths.TypeCPartnerUevent),
425       kTypeCPartnerVidPath((uevents_paths.TypeCPartnerVidPath == nullptr)
426                                    ? typec_partner_vid_path_default
427                                    : uevents_paths.TypeCPartnerVidPath),
428       kTypeCPartnerPidPath((uevents_paths.TypeCPartnerPidPath == nullptr)
429                                    ? typec_partner_pid_path_default
430                                    : uevents_paths.TypeCPartnerPidPath),
431       uevent_fd_(-1),
432       log_fd_(-1) {}
433 
434 /* Thread function to continuously monitor uevents.
435  * Exit after kMaxConsecutiveErrors to prevent spinning. */
ListenForever()436 void UeventListener::ListenForever() {
437     constexpr int kMaxConsecutiveErrors = 10;
438     int consecutive_errors = 0;
439 
440     while (1) {
441         if (ProcessUevent()) {
442             consecutive_errors = 0;
443         } else {
444             if (++consecutive_errors >= kMaxConsecutiveErrors) {
445                 ALOGE("Too many ProcessUevent errors; exiting UeventListener.");
446                 return;
447             }
448         }
449     }
450 }
451 
452 }  // namespace pixel
453 }  // namespace google
454 }  // namespace hardware
455 }  // namespace android
456