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