• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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