Based on Android Q battery service analysis

Charging type judgment based on Android Q battery service analysis

At the beginning, let me explain my needs and the difficulties I encountered.

question

  • There is no beep and icon update when plugged in for charging
  • There is no response or prompt when plugged in for charging, but it is indeed charging

need

  • Increase the charging type judgment in the set battery and display it on the UI

1. The cause of the problem

驱动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. Overall operation process analysis

Let’s first look at a flow chart
insert image description hereand call it from the kernel layer and the HAL layer. Let’s analyze it from BatteryServer.java first.

2.1 BatteryService.java

This class inherits SystemService and belongs to a system service, so when its service is monitored, every time onStart will always be called back and executed, directly upload the code

  @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());
    }

It can be seen that the main work is to register some services, the most important thing is registerHealthCallback()toBinderService()

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");
 ...
    }

See Step 1: This method is used to determine the Health service instance to use (the "default" instance from vender or the "backup" instance from healthd). Then listen to the Health event through IHealth.registerCallback, do some init operations, and register the corresponding relationship. I will not delve into this here

The second step: HealthHalCallback()it is a very critical callback, which is used to call back the data of the Health layer to the framework layer, which contains most of the information of the battery (battery charging status, etc.), and then enter this method to see the main logic

2.1.2 HealthHalCallback

This class inherits and HealthInfoCallback.Stubuses the AIDL cross-process method to make callback actions on the battery information, so you can’t see the JNI stuff here, and add another word to listen to send it to {@link android. content.BroadcastReceiver IntentReceivers} such services.

 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();
            }
        }
    }

The main thing to look at here is update()the method, then look down

2.1.3 update

This method is that the Health layer initiates a broadcast, executes a cross-process event, and calls back the data from the upper layer. Let’s look at the code first.

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

This method is very long, including charging type judgment, broadcasting when the battery is low, broadcasting when plugging in charging/unplugging charging, LED light flashing and other functions

So go directly to the code and see the explanation in the comments

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;
        }
    }

It can be seen that if there is no prompt sound when plugged in for charging, in this code, it must be that the broadcast has not been sent out (of course it does not rule out that the broadcast has been sent out, there is a problem with the code for the charging prompt), and the problem I encountered is that there is no charging prompt sound + There is no charging prompt icon, which means that the broadcast has not been executed. Why the broadcast has not been executed is mainly because the if statement is not satisfied, and the condition for satisfying the if statement is to read the current charging method, and my problem is to read The broadcast is not sent due to the charging method

The upper layer has been roughly analyzed, let's look at the bottom layer

2.2 healthd_common.cpp

Uniform execution from the main method

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;
}

Starting from the main method, call healthd_init()and healthd_mainloop()initialize and infinite loop, remember the picture at the beginning? Among them are these two methods. The mainloop is mainly used to monitor the power change of the kernel layer and read the power information. Here, we don’t pay attention to how to read it internally; init mainly instantiates and calls its init function; let’s look at it BatteryMonitorfirst

2.2.2 healthd_battery_update

This method will call BatteryMonitor.cpp bool BatteryMonitor::update(void), mainly how to read the battery information, and the implementation of reading the battery status is in this update

static void healthd_battery_update(void) {
    
    
    Health::getImplementation()->update();
}

After calling, Health::getImplementation()->update()it will be called back to the upper layer, and then it will go to BatteryMonitor.cppthe middle

2.3 BatteryMonitor.cpp

Go directly to the init method, because the init method of this class is executed in 2.1 (main function)

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;
            }
        }
    }

 ...
}

The init method mainly implements a series of nodes for reading battery information, online, status, type, charge_counter, voltage_now ...

First look at the code above

 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;

Loop through the type and online nodes in the parent directory, and write the data of the three charging types mChargerNames. After the initialization is complete, see 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;
}

Everything is in the comments, this method is mainly to get the information of the power, status, voltage, power, etc... just mentioned readPowerSupplyType, let's take a look at it here

2.3.3 readPowerSupplyType

This method obtains data according to the type node and matches the corresponding 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);
}

At this point, even if the whole process is over, it will return to 2.2, and 2.2 will call back to the upper layer 2.1.3

3. Solutions to problems

My problem is to show charging type, AC/USB/OTG charging type etc. in settings

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;
    }
  • Step 1: read BatteryService.java#sendBatteryChangedIntentLockedthe corresponding key, the default value is unknown, and the value has been analyzed according to the overall process operation

  • Step 2: Compare the current charging status according to the status (for charging, charging, disconnecting the charging connection, unknown)

    When you see the first if statement, read BatteryService.java#sendBatteryChangedIntentLockedthe key again to determine whether it is a charging type of AC/USB/WLC/unknown, and just display it on the UI.

But, it is often unsatisfactory. The bottom layer can never read the three charging types of AC/USB/WLC. After a long time, I found out that I changed the permissions of the parent directory of the charging type, which led to the parent directory. All nodes under it cannot be read, and the permissions can be restored in init6763.rc
Finally:

​ Please correct me if the analysis is not very correct, anyone who can read it is a master

Guess you like

Origin blog.csdn.net/q1210249579/article/details/118894938