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