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