基于Android Q电池服务分析

基于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中恢复权限即可
最后:

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

猜你喜欢

转载自blog.csdn.net/q1210249579/article/details/118894938
今日推荐