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