基于Android Q的电池服务分析之充电类型判断
开局先说明一下我的需求和我遇到的难题
问题
- 插入充电没有提示音和图标更新
- 插入充电没有任何反应和提示,但是确实是在充电
需求
- 在设置的电池中增加充电类型判断并显示在UI上
1.造成问题的原因
驱动online节点无法正常读取,导致上层数据为空,造成的bug
涉及到的内容:
1.BatteryService.java
这里接收了health层发来的数据:充电广播,记录充电状态,低电量,电池高温,充电方式;发送充电广播产生充电提示音和更新充电图标
2.HealthInfo层
上层注册health服务,底层调用#healthd_battery_update,回调给上层(BatteryService)的update方法,回调过来以后携带了充电的一系列数据(包括充电方式)
3.health_common.cpp
healthd_battery_update方法:用于间接回调到上层的update,它还要调用到BatteryMonitor#BatteryMonitor::update(void)
4.BatteryMonitor.cpp
bool BatteryMonitor::update(void)方法去完成实际意义上的更新,从health_common.cpp#
static void healthd_battery_update(void) {
Health::getImplementation()->update();
}
调用到这里。
2.整体运行流程分析
先来看一张流程图
从kernel层和HAL层开始调用.那我们先从BatteryServer.java分析
2.1 BatteryService.java
该类继承了SystemService,是属于一个系统服务,所以当它服务被监听时,每次onStart会一直被回调执行,直接上代码
@Override
public void onStart() {
//注册Health并执行回调结果,Health是底层的一个对电池状态监听和获取电池信息的重要层级
registerHealthCallback();
//实例化自身
mBinderService = new BinderService();
//注册本身的服务
publishBinderService("battery", mBinderService);
//注册电池监听,当底层电池电量发生变化调用此监听,并调用update。
mBatteryPropertiesRegistrar = new BatteryPropertiesRegistrar();
//注册到Binder服务端
publishBinderService("batteryproperties", mBatteryPropertiesRegistrar);
//注册到本地服务
publishLocalService(BatteryManagerInternal.class, new LocalService());
}
可以看到主要工作是做了一些服务的注册,最重要的就是registerHealthCallback()
和BinderService()
2.1.1 registerHealthCallback
private void registerHealthCallback() {
traceBegin("HealthInitWrapper");
mHealthServiceWrapper = new HealthServiceWrapper();//第一步
mHealthHalCallback = new HealthHalCallback();//第二步
// IHealth is lazily retrieved.
try {
mHealthServiceWrapper.init(mHealthHalCallback,
new HealthServiceWrapper.IServiceManagerSupplier() {
},
new HealthServiceWrapper.IHealthSupplier() {
});
} catch (RemoteException ex) {
Slog.e(TAG, "health: cannot register callback. (RemoteException)");
throw ex.rethrowFromSystemServer();
} catch (NoSuchElementException ex) {
Slog.e(TAG, "health: cannot register callback. (no supported health HAL service)");
throw ex;
} finally {
traceEnd();
}
traceBegin("HealthInitWaitUpdate");
...
}
看到第一步 :该方法是来确定要使用的Health服务实例(来自vender的“default”实例或来自healthd的“backup”实例)。然后通过IHealth.registerCallback监听取Health事件,做一些init操作,注册相应的关系。这里对这个暂不深究
第二步:HealthHalCallback()
是一个非常关键的回调,用于将Health层的数据回调到framework层,包含了电池的大部分信息(电池充电状态,等),接着进入该方法,看看主要逻辑
2.1.2 HealthHalCallback
该类继承了HealthInfoCallback.Stub
,用AIDL跨进程方式去对电池信息做回调动作,所以在这里看不见JNI的东西,另外插个话 监听电池电量变化是用广播(ACTION_BATTERY_CHANGED BATTERY_CHANGED )形式发送给{@link android.content.BroadcastReceiver IntentReceivers}这类的服务。
private final class HealthHalCallback extends IHealthInfoCallback.Stub
implements HealthServiceWrapper.Callback {
@Override public void healthInfoChanged(android.hardware.health.V2_0.HealthInfo props) {
BatteryService.this.update(props);//Health层回调数据给上层,用广播形式通知到上层
}
// on new service registered
@Override public void onRegistration(IHealth oldService, IHealth newService,
String instance) {
if (newService == null) return;
traceBegin("HealthUnregisterCallback");
try {
if (oldService != null) {
int r = oldService.unregisterCallback(this);
if (r != Result.SUCCESS) {
Slog.w(TAG, "health: cannot unregister previous callback: " +
Result.toString(r));
}
}
} catch (RemoteException ex) {
Slog.w(TAG, "health: cannot unregister previous callback (transaction error): "
+ ex.getMessage());
} finally {
traceEnd();
}
traceBegin("HealthRegisterCallback");
try {
int r = newService.registerCallback(this);
if (r != Result.SUCCESS) {
Slog.w(TAG, "health: cannot register callback: " + Result.toString(r));
return;
}
// registerCallback does NOT guarantee that update is called
// immediately, so request a manual update here.
newService.update();//电池数据回调
} catch (RemoteException ex) {
Slog.e(TAG, "health: cannot register callback (transaction error): "
+ ex.getMessage());
} finally {
traceEnd();
}
}
}
这里主要看的就是update()
方法,接着往下看
2.1.3 update
该方法就是Health层通过发起广播,执行跨进程事件,回调到上层来的数据,先看代码
private void update(android.hardware.health.V2_0.HealthInfo info) {
...
synchronized (mLock) {
if (!mUpdatesStopped) {
// 将数据拿到赋值给mHealthInfo
mHealthInfo = info.legacy;
// Process the new values.
// 处理mHealthInfo的主要实现方法
processValuesLocked(false);
mLock.notifyAll(); // for any waiters on new info
} else {
copy(mLastHealthInfo, info.legacy);
}
}
...
}
2.1.4 processValuesLocked
该方法很长,包含了充电类型判断,电量低时的广播,插入充电/拔出充电的广播,LED灯闪烁等功能
所以直接上代码,在注释里看解释
private void processValuesLocked(boolean force) {
boolean logOutlier = false;
long dischargeDuration = 0;
//获取电池电量是否低于critical界限
mBatteryLevelCritical =
mHealthInfo.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
&& mHealthInfo.batteryLevel <= mCriticalBatteryLevel;
//判断是AC/USB/WLC中的哪一种充电方式
if (mHealthInfo.chargerAcOnline) {
mPlugType = BatteryManager.BATTERY_PLUGGED_AC;
} else if (mHealthInfo.chargerUsbOnline) {
mPlugType = BatteryManager.BATTERY_PLUGGED_USB;
} else if (mHealthInfo.chargerWirelessOnline) {
mPlugType = BatteryManager.BATTERY_PLUGGED_WIRELESS;
} else {
mPlugType = BATTERY_PLUGGED_NONE;
}
if (DEBUG) {
Slog.d(TAG, "Processing new values: "
+ "info=" + mHealthInfo
+ ", mBatteryLevelCritical=" + mBatteryLevelCritical
+ ", mPlugType=" + mPlugType);
}
// Let the battery stats keep track of the current level.
try {
mBatteryStats.setBatteryState(mHealthInfo.batteryStatus, mHealthInfo.batteryHealth,
mPlugType, mHealthInfo.batteryLevel, mHealthInfo.batteryTemperature,
mHealthInfo.batteryVoltage, mHealthInfo.batteryChargeCounter,
mHealthInfo.batteryFullCharge);
} catch (RemoteException e) {
// Should never happen.
}
//低电量关机
shutdownIfNoPowerLocked();
//电池温度过高关机
shutdownIfOverTempLocked();
//force是第一次调用时标志,如果状态有更改依然会调用下面的代码
if (force || (mHealthInfo.batteryStatus != mLastBatteryStatus ||
mHealthInfo.batteryHealth != mLastBatteryHealth ||
mHealthInfo.batteryPresent != mLastBatteryPresent ||
mHealthInfo.batteryLevel != mLastBatteryLevel ||
mPlugType != mLastPlugType ||
mHealthInfo.batteryVoltage != mLastBatteryVoltage ||
mHealthInfo.batteryTemperature != mLastBatteryTemperature ||
mHealthInfo.maxChargingCurrent != mLastMaxChargingCurrent ||
mHealthInfo.maxChargingVoltage != mLastMaxChargingVoltage ||
mHealthInfo.batteryChargeCounter != mLastChargeCounter ||
mInvalidCharger != mLastInvalidCharger)) {
//插入状态有更改,mLastPlugType是记录上一次充电方式,mPlugType是当前充电方式
if (mPlugType != mLastPlugType) {
if (mLastPlugType == BATTERY_PLUGGED_NONE) {
//没充电状态到充电状态
// discharging -> charging
mChargeStartLevel = mHealthInfo.batteryLevel;
mChargeStartTime = SystemClock.elapsedRealtime();
final LogMaker builder = new LogMaker(MetricsEvent.ACTION_CHARGE);
builder.setType(MetricsEvent.TYPE_ACTION);
builder.addTaggedData(MetricsEvent.FIELD_PLUG_TYPE, mPlugType);
builder.addTaggedData(MetricsEvent.FIELD_BATTERY_LEVEL_START,
mHealthInfo.batteryLevel);
mMetricsLogger.write(builder);
// There's no value in this data unless we've discharged at least once and the
// battery level has changed; so don't log until it does.
if (mDischargeStartTime != 0 && mDischargeStartLevel != mHealthInfo.batteryLevel) {
dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime;
logOutlier = true;
EventLog.writeEvent(EventLogTags.BATTERY_DISCHARGE, dischargeDuration,
mDischargeStartLevel, mHealthInfo.batteryLevel);
// make sure we see a discharge event before logging again
mDischargeStartTime = 0;
}
} else if (mPlugType == BATTERY_PLUGGED_NONE) {
//充电状态到未充电状态 或者开机上电
// charging -> discharging or we just powered up
mDischargeStartTime = SystemClock.elapsedRealtime();
mDischargeStartLevel = mHealthInfo.batteryLevel;
long chargeDuration = SystemClock.elapsedRealtime() - mChargeStartTime;
if (mChargeStartTime != 0 && chargeDuration != 0) {
final LogMaker builder = new LogMaker(MetricsEvent.ACTION_CHARGE);
builder.setType(MetricsEvent.TYPE_DISMISS);
builder.addTaggedData(MetricsEvent.FIELD_PLUG_TYPE, mLastPlugType);
builder.addTaggedData(MetricsEvent.FIELD_CHARGING_DURATION_MILLIS,
chargeDuration);
builder.addTaggedData(MetricsEvent.FIELD_BATTERY_LEVEL_START,
mChargeStartLevel);
builder.addTaggedData(MetricsEvent.FIELD_BATTERY_LEVEL_END,
mHealthInfo.batteryLevel);
mMetricsLogger.write(builder);
}
mChargeStartTime = 0;
}
}
// 电池状态更新
if (mHealthInfo.batteryStatus != mLastBatteryStatus ||
mHealthInfo.batteryHealth != mLastBatteryHealth ||
mHealthInfo.batteryPresent != mLastBatteryPresent ||
mPlugType != mLastPlugType) {
EventLog.writeEvent(EventLogTags.BATTERY_STATUS,
mHealthInfo.batteryStatus, mHealthInfo.batteryHealth, mHealthInfo.batteryPresent ? 1 : 0,
mPlugType, mHealthInfo.batteryTechnology);
}
// 电池电量更新
if (mHealthInfo.batteryLevel != mLastBatteryLevel) {
// Don't do this just from voltage or temperature changes, that is
// too noisy.
EventLog.writeEvent(EventLogTags.BATTERY_LEVEL,
mHealthInfo.batteryLevel, mHealthInfo.batteryVoltage, mHealthInfo.batteryTemperature);
}
// 记录电池快没电状态
if (mBatteryLevelCritical && !mLastBatteryLevelCritical &&
mPlugType == BATTERY_PLUGGED_NONE) {
// We want to make sure we log discharge cycle outliers
// if the battery is about to die.
dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime;
logOutlier = true;
}
//电量低时是否切换到低电量模式
if (!mBatteryLevelLow) {
// Should we now switch in to low battery mode?
if (mPlugType == BATTERY_PLUGGED_NONE
&& mHealthInfo.batteryStatus !=
BatteryManager.BATTERY_STATUS_UNKNOWN
&& mHealthInfo.batteryLevel <= mLowBatteryWarningLevel) {
mBatteryLevelLow = true;
}
} else {
// Should we now switch out of low battery mode?
if (mPlugType != BATTERY_PLUGGED_NONE) {
mBatteryLevelLow = false;
} else if (mHealthInfo.batteryLevel >= mLowBatteryCloseWarningLevel) {
mBatteryLevelLow = false;
} else if (force && mHealthInfo.batteryLevel >= mLowBatteryWarningLevel) {
// If being forced, the previous state doesn't matter, we will just
// absolutely check to see if we are now above the warning level.
mBatteryLevelLow = false;
}
}
mSequence++;
// Separate broadcast is sent for power connected / not connected
// since the standard intent will not wake any applications and some
// applications may want to have smart behavior based on this.
//插入充电时,发送广播,播放提示音和更新充电UI
if (mPlugType != 0 && mLastPlugType == 0) {
final Intent statusIntent = new Intent(Intent.ACTION_POWER_CONNECTED);
statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
mHandler.post(new Runnable() {
@Override
public void run() {
mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
}
});
}
//断开充电时,发送广播,播放提示音和更新充电UI
else if (mPlugType == 0 && mLastPlugType != 0) {
final Intent statusIntent = new Intent(Intent.ACTION_POWER_DISCONNECTED);
statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
mHandler.post(new Runnable() {
@Override
public void run() {
mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
}
});
}
// 低电量电池事件通知
if (shouldSendBatteryLowLocked()) {
mSentLowBatteryBroadcast = true;
final Intent statusIntent = new Intent(Intent.ACTION_BATTERY_LOW);
statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
mHandler.post(new Runnable() {
@Override
public void run() {
mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
}
});
} else if (mSentLowBatteryBroadcast &&
mHealthInfo.batteryLevel >= mLowBatteryCloseWarningLevel) {
mSentLowBatteryBroadcast = false;
final Intent statusIntent = new Intent(Intent.ACTION_BATTERY_OKAY);
statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
mHandler.post(new Runnable() {
@Override
public void run() {
mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
}
});
}
//当以上的广播完成后,该方法主要用于记录mHealthInfo的状态信息,就是电池的一些状态+电池是否充电的信息+电量等信息
//发送粘性广播给everyone
sendBatteryChangedIntentLocked();
if (mLastBatteryLevel != mHealthInfo.batteryLevel || mLastPlugType != mPlugType) {
sendBatteryLevelChangedIntentLocked();
}
//更新Led灯信息
mLed.updateLightsLocked();
// This needs to be done after sendIntent() so that we get the lastest battery stats.
if (logOutlier && dischargeDuration != 0) {
logOutlierLocked(dischargeDuration);
}
//记录上一次mHealthInfo的状态
mLastBatteryStatus = mHealthInfo.batteryStatus;
mLastBatteryHealth = mHealthInfo.batteryHealth;
mLastBatteryPresent = mHealthInfo.batteryPresent;
mLastBatteryLevel = mHealthInfo.batteryLevel;
mLastPlugType = mPlugType;
mLastBatteryVoltage = mHealthInfo.batteryVoltage;
mLastBatteryTemperature = mHealthInfo.batteryTemperature;
mLastMaxChargingCurrent = mHealthInfo.maxChargingCurrent;
mLastMaxChargingVoltage = mHealthInfo.maxChargingVoltage;
mLastChargeCounter = mHealthInfo.batteryChargeCounter;
mLastBatteryLevelCritical = mBatteryLevelCritical;
mLastInvalidCharger = mInvalidCharger;
}
}
可以看到如果插入充电没有提示音,在这代码中,一定是广播没有发送出去(当然不排除广播发出去了,充电提示语那块代码有问题),我遇到的问题就是没有充电提示音+没有充电提示图标,就是广播没执行到,为什么广播没执行到,主要是因为if语句不满足,而满足if语句的条件就是需要读取当前是哪种充电方式,而我的问题是读取不到充电方式,导致的广播未发送
上层已经大概的分析完了,接下来看底层
2.2 healthd_common.cpp
统一的从main方法开始执行
2.2.1 healthd_main
int healthd_main() {
int ret;
klog_set_level(KLOG_LEVEL);
if (!healthd_mode_ops) {
KLOG_ERROR("healthd ops not set, exiting\n");
exit(1);
}
ret = healthd_init();
if (ret) {
KLOG_ERROR("Initialization failed, exiting\n");
exit(2);
}
healthd_mainloop();
KLOG_ERROR("Main loop terminated, exiting\n");
return 3;
}
从main方法开始,调用healthd_init()
和healthd_mainloop()
进行初始化和无限循环,还记得开局的那张图吧?其中就有这两个方法,mainloop主要作用于循环监听kernel层的电量改变并读取电量信息,这里不关注内部怎么读取的;init主要实例化了BatteryMonitor
并调用了它的init函数;先接着看
2.2.2 healthd_battery_update
该方法会调用BatteryMonitor.cpp中的bool BatteryMonitor::update(void)
,主要怎么去读取电量信息,读取电量状态的实现都在这update里面
static void healthd_battery_update(void) {
Health::getImplementation()->update();
}
调用了Health::getImplementation()->update()
后会回调给上层,那接着就要走到BatteryMonitor.cpp
中了
2.3 BatteryMonitor.cpp
直接走到init方法里看看,因为在第2.1(main函数)中执行了该类的init方法
2.3.1 void BatteryMonitor::init()
void BatteryMonitor::init(struct healthd_config *hc) {
String8 path;
char pval[PROPERTY_VALUE_MAX];
mHealthdConfig = hc;
std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(POWER_SUPPLY_SYSFS_PATH), closedir);
if (dir == NULL) {
KLOG_ERROR(LOG_TAG, "Could not open %s\n", POWER_SUPPLY_SYSFS_PATH);
} else {
struct dirent* entry;
while ((entry = readdir(dir.get()))) {
const char* name = entry->d_name;
std::vector<String8>::iterator itIgnoreName;
if (!strcmp(name, ".") || !strcmp(name, ".."))
continue;
itIgnoreName = find(hc->ignorePowerSupplyNames.begin(),
hc->ignorePowerSupplyNames.end(), String8(name));
if (itIgnoreName != hc->ignorePowerSupplyNames.end())
continue;
// Look for "type" file in each subdirectory
path.clear();
path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH, name);
switch(readPowerSupplyType(path)) {
case ANDROID_POWER_SUPPLY_TYPE_AC:
case ANDROID_POWER_SUPPLY_TYPE_USB:
case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:
path.clear();
path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name);
if (access(path.string(), R_OK) == 0)
mChargerNames.add(String8(name));
break;
case ANDROID_POWER_SUPPLY_TYPE_BATTERY:
mBatteryDevicePresent = true;
if (mHealthdConfig->batteryStatusPath.isEmpty()) {
path.clear();
path.appendFormat("%s/%s/status", POWER_SUPPLY_SYSFS_PATH,
name);
if (access(path, R_OK) == 0)
mHealthdConfig->batteryStatusPath = path;
}
if (mHealthdConfig->batteryHealthPath.isEmpty()) {
path.clear();
path.appendFormat("%s/%s/health", POWER_SUPPLY_SYSFS_PATH,
name);
if (access(path, R_OK) == 0)
mHealthdConfig->batteryHealthPath = path;
}
if (mHealthdConfig->batteryPresentPath.isEmpty()) {
path.clear();
path.appendFormat("%s/%s/present", POWER_SUPPLY_SYSFS_PATH,
name);
if (access(path, R_OK) == 0)
mHealthdConfig->batteryPresentPath = path;
}
if (mHealthdConfig->batteryCapacityPath.isEmpty()) {
path.clear();
path.appendFormat("%s/%s/capacity", POWER_SUPPLY_SYSFS_PATH,
name);
if (access(path, R_OK) == 0)
mHealthdConfig->batteryCapacityPath = path;
}
if (mHealthdConfig->batteryVoltagePath.isEmpty()) {
path.clear();
path.appendFormat("%s/%s/voltage_now",
POWER_SUPPLY_SYSFS_PATH, name);
if (access(path, R_OK) == 0) {
mHealthdConfig->batteryVoltagePath = path;
}
else {
path.clear();
path.appendFormat("%s/%s/batt_vol",
POWER_SUPPLY_SYSFS_PATH, name);
if (access(path, R_OK) == 0)
mHealthdConfig->batteryVoltagePath = path;
}
}
if (mHealthdConfig->batteryFullChargePath.isEmpty()) {
path.clear();
path.appendFormat("%s/%s/charge_full",
POWER_SUPPLY_SYSFS_PATH, name);
if (access(path, R_OK) == 0)
mHealthdConfig->batteryFullChargePath = path;
}
if (mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
path.clear();
path.appendFormat("%s/%s/current_now",
POWER_SUPPLY_SYSFS_PATH, name);
if (access(path, R_OK) == 0)
mHealthdConfig->batteryCurrentNowPath = path;
}
if (mHealthdConfig->batteryCycleCountPath.isEmpty()) {
path.clear();
path.appendFormat("%s/%s/cycle_count",
POWER_SUPPLY_SYSFS_PATH, name);
if (access(path, R_OK) == 0)
mHealthdConfig->batteryCycleCountPath = path;
}
if (mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
path.clear();
path.appendFormat("%s/%s/current_avg",
POWER_SUPPLY_SYSFS_PATH, name);
if (access(path, R_OK) == 0)
mHealthdConfig->batteryCurrentAvgPath = path;
}
if (mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
path.clear();
path.appendFormat("%s/%s/charge_counter",
POWER_SUPPLY_SYSFS_PATH, name);
if (access(path, R_OK) == 0)
mHealthdConfig->batteryChargeCounterPath = path;
}
if (mHealthdConfig->batteryTemperaturePath.isEmpty()) {
path.clear();
path.appendFormat("%s/%s/temp", POWER_SUPPLY_SYSFS_PATH,
name);
if (access(path, R_OK) == 0) {
mHealthdConfig->batteryTemperaturePath = path;
}
else {
path.clear();
path.appendFormat("%s/%s/batt_temp",
POWER_SUPPLY_SYSFS_PATH, name);
if (access(path, R_OK) == 0)
mHealthdConfig->batteryTemperaturePath = path;
}
}
if (mHealthdConfig->batteryTechnologyPath.isEmpty()) {
path.clear();
path.appendFormat("%s/%s/technology",
POWER_SUPPLY_SYSFS_PATH, name);
if (access(path, R_OK) == 0)
mHealthdConfig->batteryTechnologyPath = path;
}
break;
case ANDROID_POWER_SUPPLY_TYPE_UNKNOWN:
break;
}
}
}
...
}
init方法主要实现了读取电池信息的一些列节点,online ,status,type,charge_counter,voltage_now …
先看上面的这段代码
path.clear();
path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH, name);
switch(readPowerSupplyType(path)) {
case ANDROID_POWER_SUPPLY_TYPE_AC:
case ANDROID_POWER_SUPPLY_TYPE_USB:
case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:
path.clear();
path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name);
if (access(path.string(), R_OK) == 0)
mChargerNames.add(String8(name));
break;
循环遍历父目录下的type和online节点,三种充电类型的数据都写入mChargerNames
,初始化完毕了,那就看update
2.3.2 bool BatteryMonitor::update(void)
bool BatteryMonitor::update(void) {
bool logthis;
initBatteryProperties(&props);
if (!mHealthdConfig->batteryPresentPath.isEmpty())
props.batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);
else
props.batteryPresent = mBatteryDevicePresent;
//根据初始化后的数据,来读取电池信息的信息 begin
props.batteryLevel = mBatteryFixedCapacity ?
mBatteryFixedCapacity :
getIntField(mHealthdConfig->batteryCapacityPath);
props.batteryVoltage = getIntField(mHealthdConfig->batteryVoltagePath) / 1000;
if (!mHealthdConfig->batteryCurrentNowPath.isEmpty())
props.batteryCurrent = getIntField(mHealthdConfig->batteryCurrentNowPath) / 1000;
if (!mHealthdConfig->batteryFullChargePath.isEmpty())
props.batteryFullCharge = getIntField(mHealthdConfig->batteryFullChargePath);
if (!mHealthdConfig->batteryCycleCountPath.isEmpty())
props.batteryCycleCount = getIntField(mHealthdConfig->batteryCycleCountPath);
if (!mHealthdConfig->batteryChargeCounterPath.isEmpty())
props.batteryChargeCounter = getIntField(mHealthdConfig->batteryChargeCounterPath);
props.batteryTemperature = mBatteryFixedTemperature ?
mBatteryFixedTemperature :
getIntField(mHealthdConfig->batteryTemperaturePath);
std::string buf;
if (readFromFile(mHealthdConfig->batteryStatusPath, &buf) > 0)
props.batteryStatus = getBatteryStatus(buf.c_str());
if (readFromFile(mHealthdConfig->batteryHealthPath, &buf) > 0)
props.batteryHealth = getBatteryHealth(buf.c_str());
if (readFromFile(mHealthdConfig->batteryTechnologyPath, &buf) > 0)
props.batteryTechnology = String8(buf.c_str());
double MaxPower = 0;
//根据初始化后的数据,来读取电池信息的信息 end
//根据初始化,我特意提到的片段代码中的mChargerNames
for (size_t i = 0; i < mChargerNames.size(); i++) {
String8 path;
//循环遍历三种充电类型的online节点,0:未使用该类型充电,1:使用的是该类型充电
path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH,
mChargerNames[i].string());
if (getIntField(path)) {
path.clear();
path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH,
mChargerNames[i].string());
switch(readPowerSupplyType(path)) {
case ANDROID_POWER_SUPPLY_TYPE_AC:
props.chargerAcOnline = true;
break;
case ANDROID_POWER_SUPPLY_TYPE_USB:
props.chargerUsbOnline = true;
break;
case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:
props.chargerWirelessOnline = true;
break;
default:
KLOG_WARNING(LOG_TAG, "%s: Unknown power supply type\n",
mChargerNames[i].string());
}
// 上面会根据是哪种充电类型case进行赋值true,主要方法是readPowerSupplyType,它根据type节点,去得到数据匹配对应的case
//如果是WLC充电,则其余充电置false
path.clear();
path.appendFormat("%s", WLC_51025_STATUS);
if(getIntField(path)){
props.chargerWirelessOnline = true;
props.chargerAcOnline = false;
props.chargerUsbOnline = false;
}
//获取当前最大电量,电压等信息
path.clear();
path.appendFormat("%s/%s/current_max", POWER_SUPPLY_SYSFS_PATH,
mChargerNames[i].string());
int ChargingCurrent =
(access(path.string(), R_OK) == 0) ? getIntField(path) : 0;
path.clear();
path.appendFormat("%s/%s/voltage_max", POWER_SUPPLY_SYSFS_PATH,
mChargerNames[i].string());
int ChargingVoltage =
(access(path.string(), R_OK) == 0) ? getIntField(path) :
DEFAULT_VBUS_VOLTAGE;
double power = ((double)ChargingCurrent / MILLION) *
((double)ChargingVoltage / MILLION);
if (MaxPower < power) {
props.maxChargingCurrent = ChargingCurrent;
props.maxChargingVoltage = ChargingVoltage;
MaxPower = power;
}
}
}
...
// 获取到以上信息后,更新电池状态
healthd_mode_ops->battery_update(&props);
// 返回电池是否处在充电状态
return props.chargerAcOnline | props.chargerUsbOnline |
props.chargerWirelessOnline;
}
一切都在注释里,这个方法主要是获取到电量的信息,状态,电压,电量等…刚刚提到了readPowerSupplyType
,这里我们接着看一下
2.3.3 readPowerSupplyType
该方法根据type节点去获取数据,匹配对应的case
BatteryMonitor::PowerSupplyType BatteryMonitor::readPowerSupplyType(const String8& path) {
std::string buf;
int ret;
// 根据Key去赋值value
struct sysfsStringEnumMap supplyTypeMap[] = {
{
"Unknown", ANDROID_POWER_SUPPLY_TYPE_UNKNOWN },
{
"Battery", ANDROID_POWER_SUPPLY_TYPE_BATTERY },
{
"UPS", ANDROID_POWER_SUPPLY_TYPE_AC },
{
"Mains", ANDROID_POWER_SUPPLY_TYPE_AC },
{
"USB", ANDROID_POWER_SUPPLY_TYPE_USB },
{
"USB_DCP", ANDROID_POWER_SUPPLY_TYPE_AC },
{
"USB_HVDCP", ANDROID_POWER_SUPPLY_TYPE_AC },
{
"USB_CDP", ANDROID_POWER_SUPPLY_TYPE_AC },
{
"USB_ACA", ANDROID_POWER_SUPPLY_TYPE_AC },
{
"USB_C", ANDROID_POWER_SUPPLY_TYPE_AC },
{
"USB_PD", ANDROID_POWER_SUPPLY_TYPE_AC },
{
"USB_PD_DRP", ANDROID_POWER_SUPPLY_TYPE_USB },
{
"Wireless", ANDROID_POWER_SUPPLY_TYPE_WIRELESS },
{
NULL, 0 },
};
//读取传入的path路径
if (readFromFile(path, &buf) <= 0)
return ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
//拿到读取的数据转换为string
ret = mapSysfsString(buf.c_str(), supplyTypeMap);
if (ret < 0) {
KLOG_WARNING(LOG_TAG, "Unknown power supply type '%s'\n", buf.c_str());
ret = ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
}
// 返回对应的结构体,例如“USB” 就返回 ANDROID_POWER_SUPPLY_TYPE_USB
return static_cast<BatteryMonitor::PowerSupplyType>(ret);
}
此时整个流程就算完毕了,就回到了2.2,2.2又回调到上层的2.1.3了
3.解决问题的方法
我的问题是在设置中显示充电类型,AC/USB/OTG充电类型等
Framework # Utils.java
public static String getBatteryStatus(Resources res, Intent batteryChangedIntent) {
int status = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_STATUS,
BatteryManager.BATTERY_STATUS_UNKNOWN);//第一步
String statusString;
//第二步
if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
statusString = res.getString(R.string.battery_info_status_charging);
//show charge type by software UI,begin
int plugType = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0);
int plugTypeStringId = 0;
if (plugType ==BatteryManager.BATTERY_PLUGGED_AC) {
plugTypeStringId = R.string.battery_info_plug_type_ac;
}else if (plugType ==BatteryManager.BATTERY_PLUGGED_USB) {
plugTypeStringId = R.string.battery_info_plug_type_usb;
}else if (plugType ==BatteryManager.BATTERY_PLUGGED_WIRELESS) {
plugTypeStringId = R.string.battery_info_plug_type_wireless;
}
if (plugTypeStringId != 0){
statusString = String.format("%s(%s)",statusString,res.getString(plugTypeStringId));
}
// show charge type by software UI,end
} else if (status == BatteryManager.BATTERY_STATUS_DISCHARGING) {
statusString = res.getString(R.string.battery_info_status_discharging);
} else if (status == BatteryManager.BATTERY_STATUS_NOT_CHARGING) {
statusString = res.getString(R.string.battery_info_status_not_charging);
} else if (status == BatteryManager.BATTERY_STATUS_FULL) {
statusString = res.getString(R.string.battery_info_status_full);
} else {
statusString = res.getString(R.string.battery_info_status_unknown);
}
return statusString;
}
-
第一步:读取
BatteryService.java#sendBatteryChangedIntentLocked
中相应的key,默认value是未知,value已经根据在 整体流程运行中 分析出来了 -
第二步:根据status去比对当前充电状态(为充电,正在充电,断开充电连接,未知)
看到第一个if语句,再次去读取
BatteryService.java#sendBatteryChangedIntentLocked
中的key,判断是否是AC/USB/WLC/未知 中的哪一种充电类型,在UI上显示即可。
But,往往不尽人意,底层始终读取不了AC/USB/WLC三种充电类型,搞了很久很久,才发现,我特喵的把充电类型的父目录的权限给改变了,才导致父目录下的所有节点无法读取,在init6763.rc中恢复权限即可
最后:
如果有分析不是很正确的地方还请指正,能够看完的都是人上人