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