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