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