1 /*
2 * Copyright (C) 2018 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 #include "BatteryRechargingControl.h"
18
19 namespace device {
20 namespace google {
21 namespace crosshatch {
22 namespace health {
23
24 const std::string kChargerStatus = "sys/class/power_supply/battery/status";
25
26 static const std::string kStatusIsFull = "Full";
27 static const std::string kStatusIsCharging = "Charging";
28 static constexpr int kTransitionTime = 15 * 60; // Seconds
29 static constexpr int kFullSoc = 100;
30
BatteryRechargingControl()31 BatteryRechargingControl::BatteryRechargingControl() {
32 state_ = INACTIVE;
33 recharge_soc_ = 0;
34 }
35
mapSysfsString(const char * str,struct sysfsStringEnumMap map[])36 int BatteryRechargingControl::mapSysfsString(const char *str, struct sysfsStringEnumMap map[]) {
37 for (int i = 0; map[i].s; i++)
38 if (!strncmp(str, map[i].s, strlen(map[i].s)))
39 return map[i].val;
40
41 return -1;
42 }
43
getBatteryStatus(const char * status)44 int BatteryRechargingControl::getBatteryStatus(const char *status) {
45 int ret;
46 struct sysfsStringEnumMap batteryStatusMap[] = {
47 {"Unknown", android::BATTERY_STATUS_UNKNOWN},
48 {"Charging", android::BATTERY_STATUS_CHARGING},
49 {"Discharging", android::BATTERY_STATUS_DISCHARGING},
50 {"Not charging", android::BATTERY_STATUS_NOT_CHARGING},
51 {"Full", android::BATTERY_STATUS_FULL},
52 {NULL, 0},
53 };
54
55 ret = mapSysfsString(status, batteryStatusMap);
56 if (ret < 0) {
57 LOG(ERROR) << "Unknown battery status: " << status;
58 ret = android::BATTERY_STATUS_UNKNOWN;
59 }
60
61 return ret;
62 }
63
getTime(void)64 int64_t BatteryRechargingControl::getTime(void) {
65 return nanoseconds_to_seconds(systemTime(SYSTEM_TIME_BOOTTIME));
66 }
67
RemapSOC(int soc)68 int BatteryRechargingControl::RemapSOC(int soc) {
69 double diff_sec = getTime() - start_time_;
70 double ret_soc =
71 round(soc * (diff_sec / kTransitionTime) + kFullSoc * (1 - (diff_sec / kTransitionTime)));
72 LOG(INFO) << "RemapSOC: " << ret_soc;
73 return ret_soc;
74 }
75
updateBatteryProperties(struct android::BatteryProperties * props)76 void BatteryRechargingControl::updateBatteryProperties(struct android::BatteryProperties *props) {
77 std::string charger_status;
78 double elapsed_time;
79 int cur_soc;
80
81 if (!android::base::ReadFileToString(kChargerStatus, &charger_status)) {
82 LOG(ERROR) << "Cannot read the charger status";
83 return;
84 }
85
86 charger_status = android::base::Trim(charger_status);
87 props->batteryStatus = getBatteryStatus(charger_status.c_str());
88
89 if ((state_ == INACTIVE) && (props->batteryLevel < kFullSoc))
90 return;
91
92 LOG(INFO) << "Entry state_: " << state_ << " charger_status: " << charger_status
93 << " batteryLevel: " << props->batteryLevel;
94 switch (state_) {
95 case INACTIVE:
96 state_ = WAIT_EOC;
97 recharge_soc_ = 0;
98 case WAIT_EOC:
99 if (charger_status == kStatusIsFull) {
100 state_ = RECHARGING_CYCLE;
101 props->batteryLevel = kFullSoc;
102 } else if (charger_status != kStatusIsCharging) {
103 // charging stopped, assume no more power source
104 start_time_ = getTime();
105 state_ = NO_POWER_SOURCE;
106 props->batteryLevel = RemapSOC(props->batteryLevel);
107 }
108 break;
109 case RECHARGING_CYCLE:
110 if (charger_status == kStatusIsFull) {
111 recharge_soc_ = 0;
112 props->batteryLevel = kFullSoc;
113 break;
114 } else if (charger_status == kStatusIsCharging) {
115 // Recharging cycle start.
116 if (recharge_soc_ == 0) {
117 recharge_soc_ = props->batteryLevel;
118 props->batteryLevel = kFullSoc;
119 } else {
120 if (props->batteryLevel < recharge_soc_) {
121 // overload condition
122 start_time_ = getTime();
123 state_ = OVER_LOADING;
124 props->batteryLevel = RemapSOC(props->batteryLevel);
125 } else {
126 props->batteryLevel = kFullSoc;
127 }
128 }
129 } else {
130 // charging stopped, assume no more power source
131 start_time_ = getTime();
132 state_ = NO_POWER_SOURCE;
133 props->batteryLevel = RemapSOC(props->batteryLevel);
134 }
135 break;
136 case OVER_LOADING:
137 case NO_POWER_SOURCE:
138 cur_soc = props->batteryLevel;
139 elapsed_time = getTime() - start_time_;
140 if (elapsed_time > kTransitionTime) {
141 LOG(INFO) << "Time is up, leave remap";
142 state_ = INACTIVE;
143 break;
144 } else {
145 LOG(INFO) << "Diff time: " << elapsed_time;
146 int battery_level = RemapSOC(props->batteryLevel);
147 if ((battery_level == props->batteryLevel) && (battery_level != kFullSoc)) {
148 state_ = INACTIVE;
149 break;
150 }
151 props->batteryLevel = battery_level;
152 }
153 if (charger_status == kStatusIsCharging) {
154 if ((props->batteryLevel == kFullSoc) && (cur_soc >= recharge_soc_)) {
155 // When user plug in charger and the ret_soc is still 100%
156 // Change condition to Recharging cycle to avoid the SOC
157 // show lower than 100%. (Keep 100%)
158 state_ = RECHARGING_CYCLE;
159 recharge_soc_ = props->batteryLevel;
160 }
161 }
162 break;
163 default:
164 state_ = WAIT_EOC;
165 break;
166 }
167 LOG(INFO) << "Exit state_: " << state_ << " batteryLevel: " << props->batteryLevel;
168 }
169
170 } // namespace health
171 } // namespace crosshatch
172 } // namespace google
173 } // namespace device
174