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