1 /*
2 * Copyright (C) 2020 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 #define LOG_TAG "dChargerDetect"
18
19 #include <android-base/file.h>
20 #include <android-base/parseint.h>
21 #include <android-base/strings.h>
22 #include <cutils/klog.h>
23 #include <dirent.h>
24 #include <pixelhealth/ChargerDetect.h>
25 #include <pixelhealth/HealthHelper.h>
26
27 #include <string>
28
29 constexpr char kPowerSupplySysfsPath[]{"/sys/class/power_supply/"};
30 constexpr char kUsbOnlinePath[]{"/sys/class/power_supply/usb/online"};
31 constexpr char kUsbPowerSupplySysfsPath[]{"/sys/class/power_supply/usb/usb_type"};
32 constexpr char kTcpmPsyFilter[]{"tcpm"};
33 using aidl::android::hardware::health::HealthInfo;
34 using android::BatteryMonitor;
35
36 namespace hardware {
37 namespace google {
38 namespace pixel {
39 namespace health {
40
readFromFile(const std::string & path,std::string * buf)41 int ChargerDetect::readFromFile(const std::string& path, std::string* buf) {
42 if (android::base::ReadFileToString(path.c_str(), buf)) {
43 *buf = android::base::Trim(*buf);
44 }
45 return buf->length();
46 }
47
getIntField(const std::string & path)48 int ChargerDetect::getIntField(const std::string& path) {
49 std::string buf;
50 int value = 0;
51
52 if (readFromFile(path, &buf) > 0)
53 android::base::ParseInt(buf, &value);
54
55 return value;
56 }
57
58 /*
59 * Traverses through /sys/class/power_supply/ to identify TCPM(Type-C/PD) power supply.
60 * TCPM power supply's name follows the format "tcpm-source-psy-6-0025" with i2c/i3c bus id
61 * and client id(SID) baked in.
62 */
populateTcpmPsyName(std::string * tcpmPsyName)63 void ChargerDetect::populateTcpmPsyName(std::string* tcpmPsyName) {
64 std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(kPowerSupplySysfsPath), closedir);
65 if (dir == NULL) {
66 KLOG_ERROR(LOG_TAG, "Could not open %s\n", kPowerSupplySysfsPath);
67 } else {
68 struct dirent* entry;
69
70 while ((entry = readdir(dir.get()))) {
71 const char* name = entry->d_name;
72
73 KLOG_DEBUG(LOG_TAG, "Psy name:%s", name);
74 if (strstr(name, kTcpmPsyFilter)) {
75 *tcpmPsyName = name;
76 }
77 }
78 }
79 }
80
81 /*
82 * The contents of /sys/class/power_supply/<Power supply name>/usb_type follows the format:
83 * Unknown [SDP] CDP DCP
84 * with the current selected value encloses within square braces.
85 * This functions extracts the current selected value and returns it back to the caller.
86 */
getPsyUsbType(const std::string & path,std::string * type)87 int ChargerDetect::getPsyUsbType(const std::string& path, std::string* type) {
88 size_t start;
89 std::string usbType;
90 int ret;
91
92 ret = readFromFile(path, &usbType);
93 if (ret <= 0) {
94 KLOG_ERROR(LOG_TAG, "Error reading %s: %d\n", path.c_str(), ret);
95 return -EINVAL;
96 }
97
98 start = usbType.find('[');
99 if (start == std::string::npos) {
100 KLOG_ERROR(LOG_TAG, "'[' not found in %s: %s\n", path.c_str(), usbType.c_str());
101 return -EINVAL;
102 }
103
104 *type = usbType.substr(start + 1, usbType.find(']') - start - 1);
105 return 0;
106 }
107
108 /*
109 * Reads the usb power_supply's usb_type and the tcpm power_supply's usb_type to infer
110 * HealthInfo(hardware/interfaces/health/1.0/types.hal) online property.
111 */
onlineUpdate(HealthInfo * health_info)112 void ChargerDetect::onlineUpdate(HealthInfo *health_info) {
113 std::string tcpmOnlinePath, usbPsyType;
114 static std::string tcpmPsyName;
115 int ret;
116
117 health_info->chargerAcOnline = false;
118 health_info->chargerUsbOnline = false;
119
120 if (tcpmPsyName.empty()) {
121 populateTcpmPsyName(&tcpmPsyName);
122 KLOG_DEBUG(LOG_TAG, "TcpmPsyName:%s\n", tcpmPsyName.c_str());
123 }
124
125 if (!getIntField(kUsbOnlinePath)) {
126 return;
127 }
128
129 ret = getPsyUsbType(kUsbPowerSupplySysfsPath, &usbPsyType);
130 if (!ret) {
131 if (usbPsyType == "CDP" || usbPsyType == "DCP") {
132 health_info->chargerAcOnline = true;
133 return;
134 } else if (usbPsyType == "SDP") {
135 health_info->chargerUsbOnline = true;
136 return;
137 }
138 }
139
140 /* Safe to assume AC charger here if BC1.2 non compliant */
141 health_info->chargerAcOnline = true;
142
143 if (tcpmPsyName.empty()) {
144 return;
145 }
146
147 ret = getPsyUsbType(std::string(kPowerSupplySysfsPath) + tcpmPsyName + "/usb_type",
148 &usbPsyType);
149 if (ret < 0) {
150 return;
151 }
152
153 KLOG_DEBUG(LOG_TAG, "TcpmPsy Usbtype:%s\n", usbPsyType.c_str());
154
155 return;
156 }
157
onlineUpdate(struct android::BatteryProperties * props)158 void ChargerDetect::onlineUpdate(struct android::BatteryProperties *props) {
159 HealthInfo health_info = ToHealthInfo(props);
160 onlineUpdate(&health_info);
161 // Propagate the changes to props
162 props->chargerAcOnline = health_info.chargerAcOnline;
163 props->chargerUsbOnline = health_info.chargerUsbOnline;
164 // onlineUpdate doesn't change chargerWirelessOnline and other fields.
165 }
166
167 } // namespace health
168 } // namespace pixel
169 } // namespace google
170 } // namespace hardware
171