You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
174 lines
6.2 KiB
174 lines
6.2 KiB
/*
|
|
* Copyright (C) 2018 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "BatteryRechargingControl.h"
|
|
|
|
namespace device {
|
|
namespace google {
|
|
namespace crosshatch {
|
|
namespace health {
|
|
|
|
const std::string kChargerStatus = "sys/class/power_supply/battery/status";
|
|
|
|
static const std::string kStatusIsFull = "Full";
|
|
static const std::string kStatusIsCharging = "Charging";
|
|
static constexpr int kTransitionTime = 15 * 60; // Seconds
|
|
static constexpr int kFullSoc = 100;
|
|
|
|
BatteryRechargingControl::BatteryRechargingControl() {
|
|
state_ = INACTIVE;
|
|
recharge_soc_ = 0;
|
|
}
|
|
|
|
int BatteryRechargingControl::mapSysfsString(const char *str, struct sysfsStringEnumMap map[]) {
|
|
for (int i = 0; map[i].s; i++)
|
|
if (!strncmp(str, map[i].s, strlen(map[i].s)))
|
|
return map[i].val;
|
|
|
|
return -1;
|
|
}
|
|
|
|
int BatteryRechargingControl::getBatteryStatus(const char *status) {
|
|
int ret;
|
|
struct sysfsStringEnumMap batteryStatusMap[] = {
|
|
{"Unknown", android::BATTERY_STATUS_UNKNOWN},
|
|
{"Charging", android::BATTERY_STATUS_CHARGING},
|
|
{"Discharging", android::BATTERY_STATUS_DISCHARGING},
|
|
{"Not charging", android::BATTERY_STATUS_NOT_CHARGING},
|
|
{"Full", android::BATTERY_STATUS_FULL},
|
|
{NULL, 0},
|
|
};
|
|
|
|
ret = mapSysfsString(status, batteryStatusMap);
|
|
if (ret < 0) {
|
|
LOG(ERROR) << "Unknown battery status: " << status;
|
|
ret = android::BATTERY_STATUS_UNKNOWN;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int64_t BatteryRechargingControl::getTime(void) {
|
|
return nanoseconds_to_seconds(systemTime(SYSTEM_TIME_BOOTTIME));
|
|
}
|
|
|
|
int BatteryRechargingControl::RemapSOC(int soc) {
|
|
double diff_sec = getTime() - start_time_;
|
|
double ret_soc =
|
|
round(soc * (diff_sec / kTransitionTime) + kFullSoc * (1 - (diff_sec / kTransitionTime)));
|
|
LOG(INFO) << "RemapSOC: " << ret_soc;
|
|
return ret_soc;
|
|
}
|
|
|
|
void BatteryRechargingControl::updateBatteryProperties(struct android::BatteryProperties *props) {
|
|
std::string charger_status;
|
|
double elapsed_time;
|
|
int cur_soc;
|
|
|
|
if (!android::base::ReadFileToString(kChargerStatus, &charger_status)) {
|
|
LOG(ERROR) << "Cannot read the charger status";
|
|
return;
|
|
}
|
|
|
|
charger_status = android::base::Trim(charger_status);
|
|
props->batteryStatus = getBatteryStatus(charger_status.c_str());
|
|
|
|
if ((state_ == INACTIVE) && (props->batteryLevel < kFullSoc))
|
|
return;
|
|
|
|
LOG(INFO) << "Entry state_: " << state_ << " charger_status: " << charger_status
|
|
<< " batteryLevel: " << props->batteryLevel;
|
|
switch (state_) {
|
|
case INACTIVE:
|
|
state_ = WAIT_EOC;
|
|
recharge_soc_ = 0;
|
|
case WAIT_EOC:
|
|
if (charger_status == kStatusIsFull) {
|
|
state_ = RECHARGING_CYCLE;
|
|
props->batteryLevel = kFullSoc;
|
|
} else if (charger_status != kStatusIsCharging) {
|
|
// charging stopped, assume no more power source
|
|
start_time_ = getTime();
|
|
state_ = NO_POWER_SOURCE;
|
|
props->batteryLevel = RemapSOC(props->batteryLevel);
|
|
}
|
|
break;
|
|
case RECHARGING_CYCLE:
|
|
if (charger_status == kStatusIsFull) {
|
|
recharge_soc_ = 0;
|
|
props->batteryLevel = kFullSoc;
|
|
break;
|
|
} else if (charger_status == kStatusIsCharging) {
|
|
// Recharging cycle start.
|
|
if (recharge_soc_ == 0) {
|
|
recharge_soc_ = props->batteryLevel;
|
|
props->batteryLevel = kFullSoc;
|
|
} else {
|
|
if (props->batteryLevel < recharge_soc_) {
|
|
// overload condition
|
|
start_time_ = getTime();
|
|
state_ = OVER_LOADING;
|
|
props->batteryLevel = RemapSOC(props->batteryLevel);
|
|
} else {
|
|
props->batteryLevel = kFullSoc;
|
|
}
|
|
}
|
|
} else {
|
|
// charging stopped, assume no more power source
|
|
start_time_ = getTime();
|
|
state_ = NO_POWER_SOURCE;
|
|
props->batteryLevel = RemapSOC(props->batteryLevel);
|
|
}
|
|
break;
|
|
case OVER_LOADING:
|
|
case NO_POWER_SOURCE:
|
|
cur_soc = props->batteryLevel;
|
|
elapsed_time = getTime() - start_time_;
|
|
if (elapsed_time > kTransitionTime) {
|
|
LOG(INFO) << "Time is up, leave remap";
|
|
state_ = INACTIVE;
|
|
break;
|
|
} else {
|
|
LOG(INFO) << "Diff time: " << elapsed_time;
|
|
int battery_level = RemapSOC(props->batteryLevel);
|
|
if ((battery_level == props->batteryLevel) && (battery_level != kFullSoc)) {
|
|
state_ = INACTIVE;
|
|
break;
|
|
}
|
|
props->batteryLevel = battery_level;
|
|
}
|
|
if (charger_status == kStatusIsCharging) {
|
|
if ((props->batteryLevel == kFullSoc) && (cur_soc >= recharge_soc_)) {
|
|
// When user plug in charger and the ret_soc is still 100%
|
|
// Change condition to Recharging cycle to avoid the SOC
|
|
// show lower than 100%. (Keep 100%)
|
|
state_ = RECHARGING_CYCLE;
|
|
recharge_soc_ = props->batteryLevel;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
state_ = WAIT_EOC;
|
|
break;
|
|
}
|
|
LOG(INFO) << "Exit state_: " << state_ << " batteryLevel: " << props->batteryLevel;
|
|
}
|
|
|
|
} // namespace health
|
|
} // namespace crosshatch
|
|
} // namespace google
|
|
} // namespace device
|