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 "BatteryDefender"
18
19 #include <pixelhealth/BatteryDefender.h>
20
21 #include <android-base/file.h>
22 #include <android-base/logging.h>
23 #include <android-base/parsebool.h>
24 #include <android-base/parseint.h>
25 #include <android-base/properties.h>
26
27 #include <time.h>
28 #include <utils/Timers.h>
29
30 #include <cutils/klog.h>
31
32 namespace hardware {
33 namespace google {
34 namespace pixel {
35 namespace health {
36
BatteryDefender(const char * pathWirelessPresent,const char * pathChargeLevelStart,const char * pathChargeLevelStop,const int32_t timeToActivateSecs,const int32_t timeToClearTimerSecs)37 BatteryDefender::BatteryDefender(const char *pathWirelessPresent, const char *pathChargeLevelStart,
38 const char *pathChargeLevelStop, const int32_t timeToActivateSecs,
39 const int32_t timeToClearTimerSecs)
40
41 : kPathWirelessPresent(pathWirelessPresent),
42 kPathChargeLevelStart(pathChargeLevelStart),
43 kPathChargeLevelStop(pathChargeLevelStop),
44 kTimeToActivateSecs(timeToActivateSecs),
45 kTimeToClearTimerSecs(timeToClearTimerSecs) {
46 mTimePreviousSecs = getTime();
47 }
48
clearStateData(void)49 void BatteryDefender::clearStateData(void) {
50 mHasReachedHighCapacityLevel = false;
51 mTimeActiveSecs = 0;
52 mTimeChargerNotPresentSecs = 0;
53 mTimeChargerPresentSecs = 0;
54 }
55
loadPersistentStorage(void)56 void BatteryDefender::loadPersistentStorage(void) {
57 if (mIsPowerAvailable) {
58 // Load accumulated time from persisted storage
59 mTimeChargerPresentSecs = readFileToInt(kPathPersistChargerPresentTime);
60 mTimeActiveSecs = readFileToInt(kPathPersistDefenderActiveTime);
61 }
62 }
63
getTime(void)64 int64_t BatteryDefender::getTime(void) {
65 return nanoseconds_to_seconds(systemTime(SYSTEM_TIME_BOOTTIME));
66 }
67
getDeltaTimeSeconds(int64_t * timeStartSecs)68 int64_t BatteryDefender::getDeltaTimeSeconds(int64_t *timeStartSecs) {
69 const int64_t timeCurrentSecs = getTime();
70 const int64_t timePreviousSecs = *timeStartSecs;
71 *timeStartSecs = timeCurrentSecs;
72 return timeCurrentSecs - timePreviousSecs;
73 }
74
removeLineEndings(std::string * str)75 void BatteryDefender::removeLineEndings(std::string *str) {
76 str->erase(std::remove(str->begin(), str->end(), '\n'), str->end());
77 str->erase(std::remove(str->begin(), str->end(), '\r'), str->end());
78 }
79
readFileToInt(const char * path,const bool optionalFile)80 int BatteryDefender::readFileToInt(const char *path, const bool optionalFile) {
81 std::string buffer;
82 int value = 0; // default
83 if (!android::base::ReadFileToString(path, &buffer)) {
84 if (optionalFile == false) {
85 LOG(ERROR) << "Failed to read " << path;
86 }
87 } else {
88 removeLineEndings(&buffer);
89 if (!android::base::ParseInt(buffer.c_str(), &value)) {
90 LOG(ERROR) << "Failed to parse " << path;
91 }
92 }
93
94 return value;
95 }
96
writeIntToFile(const char * path,const int value)97 bool BatteryDefender::writeIntToFile(const char *path, const int value) {
98 bool success = android::base::WriteStringToFile(std::to_string(value), path);
99 if (!success) {
100 LOG(ERROR) << "Failed to write " << path;
101 }
102
103 return success;
104 }
105
writeTimeToFile(const char * path,const int value,int64_t * previous)106 void BatteryDefender::writeTimeToFile(const char *path, const int value, int64_t *previous) {
107 // 30 second delay before repeated writes
108 const bool hasTimeChangedSignificantly = ((value == 0) || (*previous == -1) ||
109 (value > *previous + 30) || (value < *previous - 30));
110 if ((value != *previous) && hasTimeChangedSignificantly) {
111 writeIntToFile(path, value);
112 *previous = value;
113 }
114 }
115
writeChargeLevelsToFile(const int vendorStart,const int vendorStop)116 void BatteryDefender::writeChargeLevelsToFile(const int vendorStart, const int vendorStop) {
117 int chargeLevelStart = vendorStart;
118 int chargeLevelStop = vendorStop;
119 if (mCurrentState == STATE_ACTIVE) {
120 const int newDefenderLevelStart = android::base::GetIntProperty(
121 kPropBatteryDefenderCtrlStartSOC, kChargeLevelDefenderStart, 0, 100);
122 const int newDefenderLevelStop = android::base::GetIntProperty(
123 kPropBatteryDefenderCtrlStopSOC, kChargeLevelDefenderStop, 0, 100);
124 const bool overrideLevelsValid =
125 (newDefenderLevelStart <= newDefenderLevelStop) && (newDefenderLevelStop != 0);
126
127 if (overrideLevelsValid) {
128 chargeLevelStart = newDefenderLevelStart;
129 chargeLevelStop = newDefenderLevelStop;
130 } else {
131 chargeLevelStart = kChargeLevelDefenderStart;
132 chargeLevelStop = kChargeLevelDefenderStop;
133 }
134 }
135
136 // Disable battery defender effects in charger mode until
137 // b/149598262 is resolved
138 if (android::base::GetProperty(kPropBootmode, "undefined") != "charger") {
139 if (chargeLevelStart != mChargeLevelStartPrevious) {
140 if (writeIntToFile(kPathChargeLevelStart, chargeLevelStart)) {
141 mChargeLevelStartPrevious = chargeLevelStart;
142 }
143 }
144 if (chargeLevelStop != mChargeLevelStopPrevious) {
145 if (writeIntToFile(kPathChargeLevelStop, chargeLevelStop)) {
146 mChargeLevelStopPrevious = chargeLevelStop;
147 }
148 }
149 }
150 }
151
isChargePowerAvailable(void)152 bool BatteryDefender::isChargePowerAvailable(void) {
153 // USB presence is an indicator of power availability
154 const bool chargerPresentWired = readFileToInt(kPathUSBChargerPresent) != 0;
155 const bool chargerPresentWireless =
156 readFileToInt(kPathWirelessPresent, mIgnoreWirelessFileError) != 0;
157 mIsUsbPresent = chargerPresentWired;
158 mIsWirelessPresent = chargerPresentWireless;
159
160 // Report wireless read error only once; some devices may not have a wireless adapter
161 mIgnoreWirelessFileError = true;
162
163 return chargerPresentWired || chargerPresentWireless;
164 }
165
isDefaultChargeLevel(const int start,const int stop)166 bool BatteryDefender::isDefaultChargeLevel(const int start, const int stop) {
167 return ((start == kChargeLevelDefaultStart) && (stop == kChargeLevelDefaultStop));
168 }
169
isBatteryDefenderDisabled(const int vendorStart,const int vendorStop)170 bool BatteryDefender::isBatteryDefenderDisabled(const int vendorStart, const int vendorStop) {
171 const bool isDefaultVendorChargeLevel = isDefaultChargeLevel(vendorStart, vendorStop);
172 const bool isOverrideDisabled =
173 android::base::GetBoolProperty(kPropBatteryDefenderDisable, false);
174 const bool isCtrlEnabled =
175 android::base::GetBoolProperty(kPropBatteryDefenderCtrlEnable, kDefaultEnable);
176
177 return isOverrideDisabled || (isDefaultVendorChargeLevel == false) || (isCtrlEnabled == false);
178 }
179
addTimeToChargeTimers(void)180 void BatteryDefender::addTimeToChargeTimers(void) {
181 if (mIsPowerAvailable) {
182 if (mHasReachedHighCapacityLevel) {
183 mTimeChargerPresentSecs += mTimeBetweenUpdateCalls;
184 }
185 mTimeChargerNotPresentSecs = 0;
186 } else {
187 mTimeChargerNotPresentSecs += mTimeBetweenUpdateCalls;
188 }
189 }
190
getTimeToActivate(void)191 int32_t BatteryDefender::getTimeToActivate(void) {
192 // Use the default constructor value if the modified property is not between 60 and INT_MAX
193 // (seconds)
194 const int32_t timeToActivateOverride =
195 android::base::GetIntProperty(kPropBatteryDefenderThreshold, kTimeToActivateSecs,
196 (int32_t)ONE_MIN_IN_SECONDS, INT32_MAX);
197
198 const bool overrideActive = timeToActivateOverride != kTimeToActivateSecs;
199 if (overrideActive) {
200 return timeToActivateOverride;
201 } else {
202 // No overrides taken; apply ctrl time to activate...
203 // Note; do not allow less than 1 day trigger time
204 return android::base::GetIntProperty(kPropBatteryDefenderCtrlActivateTime,
205 kTimeToActivateSecs, (int32_t)ONE_DAY_IN_SECONDS,
206 INT32_MAX);
207 }
208 }
209
stateMachine_runAction(const state_E state,const struct android::BatteryProperties * props)210 void BatteryDefender::stateMachine_runAction(const state_E state,
211 const struct android::BatteryProperties *props) {
212 switch (state) {
213 case STATE_INIT:
214 loadPersistentStorage();
215 if (props->chargerUsbOnline || props->chargerAcOnline) {
216 mWasAcOnline = props->chargerAcOnline;
217 mWasUsbOnline = props->chargerUsbOnline;
218 }
219 break;
220
221 case STATE_DISABLED:
222 case STATE_DISCONNECTED:
223 clearStateData();
224 break;
225
226 case STATE_CONNECTED: {
227 addTimeToChargeTimers();
228
229 const int triggerLevel = android::base::GetIntProperty(
230 kPropBatteryDefenderCtrlTriggerSOC, kChargeHighCapacityLevel, 0, 100);
231 if (props->batteryLevel >= triggerLevel) {
232 mHasReachedHighCapacityLevel = true;
233 }
234 } break;
235
236 case STATE_ACTIVE:
237 addTimeToChargeTimers();
238 mTimeActiveSecs += mTimeBetweenUpdateCalls;
239 break;
240
241 default:
242 break;
243 }
244
245 // Must be loaded after init has set the property
246 mTimeToActivateSecsModified = getTimeToActivate();
247 }
248
stateMachine_getNextState(const state_E state)249 BatteryDefender::state_E BatteryDefender::stateMachine_getNextState(const state_E state) {
250 state_E nextState = state;
251
252 if (mIsDefenderDisabled) {
253 nextState = STATE_DISABLED;
254 } else {
255 switch (state) {
256 case STATE_INIT:
257 if (mIsPowerAvailable) {
258 if (mTimeChargerPresentSecs > mTimeToActivateSecsModified) {
259 nextState = STATE_ACTIVE;
260 } else {
261 nextState = STATE_CONNECTED;
262 }
263 } else {
264 nextState = STATE_DISCONNECTED;
265 }
266 break;
267
268 case STATE_DISABLED:
269 nextState = STATE_DISCONNECTED;
270 break;
271
272 case STATE_DISCONNECTED:
273 if (mIsPowerAvailable) {
274 nextState = STATE_CONNECTED;
275 }
276 break;
277
278 case STATE_CONNECTED:
279 if (mTimeChargerPresentSecs > mTimeToActivateSecsModified) {
280 nextState = STATE_ACTIVE;
281 }
282 FALLTHROUGH_INTENDED;
283
284 case STATE_ACTIVE: {
285 const int timeToClear = android::base::GetIntProperty(
286 kPropBatteryDefenderCtrlResumeTime, kTimeToClearTimerSecs, 0, INT32_MAX);
287
288 /* Check for mIsPowerAvailable in case timeToClear is 0 */
289 if ((mTimeChargerNotPresentSecs >= timeToClear) && (mIsPowerAvailable == false)) {
290 nextState = STATE_DISCONNECTED;
291 }
292 } break;
293
294 default:
295 break;
296 }
297 }
298
299 return nextState;
300 }
301
302 // This will run once at the rising edge of a new state transition,
303 // in addition to runAction()
stateMachine_firstAction(const state_E state)304 void BatteryDefender::stateMachine_firstAction(const state_E state) {
305 switch (state) {
306 case STATE_DISABLED:
307 LOG(INFO) << "Disabled!";
308 FALLTHROUGH_INTENDED;
309
310 case STATE_DISCONNECTED:
311 clearStateData();
312 break;
313
314 case STATE_CONNECTED:
315 // Time already accumulated on state transition implies that there has
316 // already been a full charge cycle (this could happen on boot).
317 if (mTimeChargerPresentSecs > 0) {
318 mHasReachedHighCapacityLevel = true;
319 }
320 break;
321
322 case STATE_ACTIVE:
323 mHasReachedHighCapacityLevel = true;
324 LOG(INFO) << "Started with " << mTimeChargerPresentSecs
325 << " seconds of power availability!";
326 break;
327
328 case STATE_INIT:
329 default:
330 // No actions
331 break;
332 }
333 }
334
updateDefenderProperties(struct android::BatteryProperties * props)335 void BatteryDefender::updateDefenderProperties(struct android::BatteryProperties *props) {
336 /**
337 * Override the OVERHEAT flag for UI updates to settings.
338 * Also, force AC/USB online if active and still connected to power.
339 */
340 if (mCurrentState == STATE_ACTIVE) {
341 props->batteryHealth = android::BATTERY_HEALTH_OVERHEAT;
342 }
343
344 /**
345 * If the kernel is forcing the input current limit to 0, then the online status may
346 * need to be overwritten. Also, setting a charge limit below the current charge level
347 * may disable the adapter.
348 * Note; only override "online" if necessary (all "online"s are false).
349 */
350 if (props->chargerUsbOnline == false && props->chargerAcOnline == false) {
351 /* Override if the USB is connected and a battery defender is active */
352 if (mIsUsbPresent && props->batteryHealth == android::BATTERY_HEALTH_OVERHEAT) {
353 if (mWasAcOnline) {
354 props->chargerAcOnline = true;
355 }
356 if (mWasUsbOnline) {
357 props->chargerUsbOnline = true;
358 }
359 }
360 } else {
361 /* One of these booleans will always be true if updated here */
362 mWasAcOnline = props->chargerAcOnline;
363 mWasUsbOnline = props->chargerUsbOnline;
364 }
365
366 /* Do the same as above for wireless adapters */
367 if (props->chargerWirelessOnline == false) {
368 if (mIsWirelessPresent && props->batteryHealth == android::BATTERY_HEALTH_OVERHEAT) {
369 props->chargerWirelessOnline = true;
370 }
371 }
372 }
373
update(struct android::BatteryProperties * props)374 void BatteryDefender::update(struct android::BatteryProperties *props) {
375 if (!props) {
376 return;
377 }
378
379 // Update module inputs
380 const int chargeLevelVendorStart =
381 android::base::GetIntProperty(kPropChargeLevelVendorStart, kChargeLevelDefaultStart);
382 const int chargeLevelVendorStop =
383 android::base::GetIntProperty(kPropChargeLevelVendorStop, kChargeLevelDefaultStop);
384 mIsDefenderDisabled = isBatteryDefenderDisabled(chargeLevelVendorStart, chargeLevelVendorStop);
385 mIsPowerAvailable = isChargePowerAvailable();
386 mTimeBetweenUpdateCalls = getDeltaTimeSeconds(&mTimePreviousSecs);
387
388 // Run state machine
389 stateMachine_runAction(mCurrentState, props);
390 const state_E nextState = stateMachine_getNextState(mCurrentState);
391 if (nextState != mCurrentState) {
392 stateMachine_firstAction(nextState);
393 }
394 mCurrentState = nextState;
395
396 // Verify/update battery defender battery properties
397 updateDefenderProperties(props); /* May override battery properties */
398
399 // Store outputs
400 writeTimeToFile(kPathPersistChargerPresentTime, mTimeChargerPresentSecs,
401 &mTimeChargerPresentSecsPrevious);
402 writeTimeToFile(kPathPersistDefenderActiveTime, mTimeActiveSecs, &mTimeActiveSecsPrevious);
403 writeChargeLevelsToFile(chargeLevelVendorStart, chargeLevelVendorStop);
404 android::base::SetProperty(kPropBatteryDefenderState, stateStringMap[mCurrentState]);
405 }
406
407 } // namespace health
408 } // namespace pixel
409 } // namespace google
410 } // namespace hardware
411