基于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会一直被回调执行,直接上代码

  @Overridepublic 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.Stubimplements 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) {// 将数据拿到赋值给mHealthInfomHealthInfo = 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 -> chargingmChargeStartLevel = 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 againmDischargeStartTime = 0;}} else if (mPlugType == BATTERY_PLUGGED_NONE) {//充电状态到未充电状态 或者开机上电// charging -> discharging or we just powered upmDischargeStartTime = 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.//插入充电时,发送广播,播放提示音和更新充电UIif (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() {@Overridepublic void run() {mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);}});}//断开充电时,发送广播,播放提示音和更新充电UIelse 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() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic void run() {mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);}});}//当以上的广播完成后,该方法主要用于记录mHealthInfo的状态信息,就是电池的一些状态+电池是否充电的信息+电量等信息//发送粘性广播给everyonesendBatteryChangedIntentLocked();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 subdirectorypath.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);elseprops.batteryPresent = mBatteryDevicePresent;
//根据初始化后的数据,来读取电池信息的信息 beginprops.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//根据初始化,我特意提到的片段代码中的mChargerNamesfor (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充电,则其余充电置falsepath.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去赋值valuestruct 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;//拿到读取的数据转换为stringret = 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_USBreturn 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,beginint 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中恢复权限即可
最后:

​ 如果有分析不是很正确的地方还请指正,能够看完的都是人上人

基于Android Q电池服务分析相关推荐

  1. 国外基于android的系统,基于Android的位置服务系统设计与实现

    摘要: 近年来,随着移动互联网的飞速发展,位置服务已经成为移动增值业务的一个重要发展方向,然而由于现代位置服务应用规模不断扩大,用户和兴趣点的数据急剧增加,降低了周围兴趣点查询服务的实用性.本文通过地 ...

  2. 基于Android社区养老服务信息平台

    21世纪的今天,随着社会的不断发展与进步,人们对于信息科学化的认识,已由低层次向高层次发展,由原来的感性认识向理性认识提高,管理工作的重要性已逐渐被人们所认识,科学化的管理,使信息存储达到准确.快速. ...

  3. 小米9android q测试版,基于Android Q的MIUI来了 小米9尝鲜

    01基于Android Q的MIUI来了 中关村在线消息:谷歌在今年五月份发布了最新的Android Q系统,虽然很多国产手机还没有升级到Android P,但是还是有不少厂家开始对旗下的手机升级到最 ...

  4. android chrome 44,[图]非隔代升级:新代码暗示Chrome OS的安卓支持将基于Android Q

    由于在更新中直接跳过了Android Oreo版本,因此外媒此前猜测Chrome OS系统会继续采用隔代升级的方案,跳过Android Q直接基于Android R.不过发布到Chromium Ger ...

  5. android 小米截图,小米工程师晒基于Android Q定制的MIUI截图

    原标题:小米工程师晒基于Android Q定制的MIUI截图 集微网7月12日消息(文/数码控),昨天小米手机系统软件部总监张国全宣布Android Q的MIUI版本已经开始内测,欢迎大家报问题!稳定 ...

  6. miui9支持android,基于Android Q的MIUI来了 小米9尝鲜

    中关村在线消息:谷歌在今年五月份发布了最新的Android Q系统,虽然很多国产手机还没有升级到Android P,但是还是有不少厂家开始对旗下的手机升级到最新的Android Q系统,根据小米最新的 ...

  7. 三星android q,三星Galaxy S11再被爆料 系统将基于Android Q

    中关村在线消息:据SamMobile消息,三星One UI 2.0将基于Android Q打造,而三星Galaxy S11将搭载One UI 2.1.SamMobile强调该消息属实. One UI ...

  8. android 重新绘制界面,基于Android Q:华为最新EMUI10界面曝光,图标重绘继续优化...

    原标题:基于Android Q:华为最新EMUI10界面曝光,图标重绘继续优化 在手机的深度定制系统里,华为EMUI应该是知名度非常高的一个.重新设计的外观和强化的功能让华为手机的使用体验得到增强,很 ...

  9. 小米8 android Q gsi,小米8 SE已基于Android Q系统进行测试

    据悉,安卓版本如今是依照26个英文字母的排列顺序进行依次推进的,那么在Android 9 Pie之后可能就是Android Q(安卓10.0?). 经查,GeekBench 4.3数据库中,小米8 S ...

最新文章

  1. 深度学习到底有哪些卷积?
  2. lua的table+setfenv+setmetatable陷阱
  3. DataStage系列教程 (Pivot_Enterprise 行列转换)
  4. Linux内核对设备树的处理
  5. Gentoo 安装日记 13 (配置内核 :可执行文件安格式和网络)
  6. html制作花样链接卡页面_花样链接卡.html
  7. 手机蓝牙如何减少延时_如何使用车载蓝牙播放手机音乐的方法
  8. CSS基本选择器、层次选择器、结构伪类选择器、属性选择器
  9. package-lock.json是做什么用的_做鱼缸用什么玻璃好?
  10. C语言树形文件结构的创建,C语言二叉树
  11. SAP S/4HANA Customer Management(CRM)模块的扩展性设计
  12. include详解 shell_socket实现基于tcp/ip的网络远程shell命令解析器(完善中...)
  13. Codeforces Round #268 (Div. 1) C. Hack it! 数位dp + 构造数位
  14. linux添加video驱动,linux下video驱动源码位置
  15. WinDbg使用摘要
  16. 【C练习】两个已经从小到大的数组合并成为一个从小到大排序的数组
  17. Visio2010安装和卸载
  18. 相克军oracle dba视频,相克军 Oracle DBA培训视频教程
  19. 从架构到监控报警,支付系统的设计如何步步为营
  20. 【安全】网络安全态势感知

热门文章

  1. 第二届国际计算成像会议-CITA
  2. CISCO XRV-9K KVM虚机启动问题
  3. 迷宫游戏python实现
  4. 公众号被处罚后排名下滑
  5. 傻子安装cobbler
  6. 关于网卡eth0、eth1以及服务器为什么要把内网和外网卡区分开
  7. 关键点检测项目代码开源了!
  8. may have been in progress in another thread when fork() was called.
  9. word里公式后面标号怎么对齐,Word里面公式后面的编号如何与公式最后一行对齐?...
  10. DE2带的IP核ISP12362报错问题解决 Error:avalon_slave_1_irq: associatedAddressablePoint out of range...