当前位置: 首页 > 工具软件 > Juice-battery > 使用案例 >

BatteryService:电池电量管理

柯永福
2023-12-01

前言:BatteryService用于获取电池信息、充电状态等,如果想对Android的功耗控制有更深入的了解,有必要分析一下BatteryService。

1.来看下BatteryService.java:

BatteryService.java

public BatteryService(Context context) {
    super(context);

    mContext = context;
    mHandler = new Handler(true /*async*/);

    //Led封装了LightsManager,不同电量下led灯的颜色就由它来控制
    mLed = new Led(context, getLocalService(LightsManager.class));

    //电池属性改变的时候,会将全部信息写到mBatteryStats中,app端从mBatteryStats中获取当前电池的电量以及状态
    mBatteryStats = BatteryStatsService.getService();
    mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);

    //以下是根据配置文件,定义不同电量对应的等级

    //电池危急的电量;当电池电量低于此值时,将强制关机,这里是5
    mCriticalBatteryLevel = mContext.getResources().getInteger(
            com.android.internal.R.integer.config_criticalBatteryWarningLevel);

    //低电警告的电量;当电池电量低于此值时,系统报警,例如闪烁LED灯等,这里是15
    mLowBatteryWarningLevel = mContext.getResources().getInteger(
            com.android.internal.R.integer.config_lowBatteryWarningLevel);

    //关闭低电警告的电量;当电池电量高于此值时,结束低电状态,停止警示灯,这里是20
    mLowBatteryCloseWarningLevel = mLowBatteryWarningLevel + mContext.getResources().getInteger(
            com.android.internal.R.integer.config_lowBatteryCloseWarningBump);

    //关闭电池的温度(温度失控,就会出现三星S7爆炸啥的......)
    mShutdownBatteryTemperature = mContext.getResources().getInteger(
            com.android.internal.R.integer.shutdownBatteryTemperature);

    // 监控终端是否连接不匹配的充电器
    if (new File("/sys/devices/virtual/switch/invalid_charger/state").exists()) {
        UEventObserver invalidChargerObserver = new UEventObserver() {
            @Override
            public void onUEvent(UEvent event) {
                final int invalidCharger = "1".equals(event.get("SWITCH_STATE")) ? 1 : 0;
                synchronized (mLock) {
                    if (mInvalidCharger != invalidCharger) {
                        mInvalidCharger = invalidCharger;
                    }
                }
            }
        };
        invalidChargerObserver.startObserving(
                "DEVPATH=/devices/virtual/switch/invalid_charger");
    }
}

(1)既然是服务,那就看下它的onStart()方法:

public void onStart() {
    //获取电源属性服务的BinderProxy对象,java层通过Binder直接与native层通信
    IBinder b = ServiceManager.getService("batteryproperties");
    final IBatteryPropertiesRegistrar batteryPropertiesRegistrar =
            IBatteryPropertiesRegistrar.Stub.asInterface(b);
    try {
        //向电源属性服务注册一个回调接口
        //当电源属性发生变化时,BatteryListener的batteryPropertiesChanged函数将被调用
        batteryPropertiesRegistrar.registerListener(new BatteryListener());
    } catch (RemoteException e) {
        // Should never happen.
    }

    mBinderService = new BinderService();
    //将自己注册到service manager进程中
    publishBinderService("battery", mBinderService);
    //以后BatteryManagerInternal接口类型的对象,只能有BatteryService的内部类LocalService一个
    publishLocalService(BatteryManagerInternal.class, new LocalService());
}

 我们使用BatteryListener监听了电池属性的变化,当电池属性改变时,native层会回调BatteryListener.batteryPropertiesChanged()方法:

private final class BatteryListener extends IBatteryPropertiesListener.Stub {
    @Override
    public void batteryPropertiesChanged(BatteryProperties props) {
        final long identity = Binder.clearCallingIdentity();
        try {
            //电源属性发生变化后,回调BatteryService的update函数
            BatteryService.this.update(props);
        } finally {
            Binder.restoreCallingIdentity(identity);
        }
    }
}

再来看下update()方法:

private void update(BatteryProperties props) {
    synchronized (mLock) {
        //从代码来看,mUpdatesStopped默认为false,通过调用onShellCommand()它的值才有可能改变
        if (!mUpdatesStopped) {
            //mBatteryProps包含了电池的所有信息,由底层传递过来
            mBatteryProps = props;
            //更新电源相关的信息
            processValuesLocked(false);
        } else {
            mLastBatteryProps.set(props);
        }
    }
}

这个processValuesLocked()函数会在后面重点分析;

 

(2)说完了BatteryService的启动过程,还需要看一下onBootPhase()函数,这个函数什么时候会被调用呢?

在SystemServiceManager中有一个startBootPhase(final int phase )函数,这个函数有啥作用呢?

android系统的启动,是分为几个阶段的,startBootPhase(final int phase)中的这个phase,就表示android系统的启动目前处于哪个阶段,SystemServiceManager将这个phase传递给每个系统服务,每个服务可以根据不同的阶段去做不同的事情;

来看下BatteryService在系统不同的启动阶段做了哪些事情:

public void onBootPhase(int phase) {
    //在PHASE_ACTIVITY_MANAGER_READY阶段后,进行部分初始化即
    if (phase == PHASE_ACTIVITY_MANAGER_READY) {
        // check our power situation now that it is safe to display the shutdown dialog.
        synchronized (mLock) {
            ContentObserver obs = new ContentObserver(mHandler) {
                @Override
                public void onChange(boolean selfChange) {
                    synchronized (mLock) {
                        updateBatteryWarningLevelLocked();
                    }
                }
            };
            final ContentResolver resolver = mContext.getContentResolver();
            //监听设置中低电量警告的电量值是否改变,改变时调用updateBatteryWarningLevelLocked函数
            resolver.registerContentObserver(Settings.Global.getUriFor(
                    Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL),
                    false, obs, UserHandle.USER_ALL);
            updateBatteryWarningLevelLocked();
        }
    }
}

onBootPhase()函数主要是注册一个监听器,检测低电量警告的电量值是否改变,然后调用updateBatteryWarningLevelLocked()函数。

接下来就开始分析processValuesLocked()和updateBatteryWarningLevelLocked()函数;

 

2.processValuesLocked()函数解析

private void processValuesLocked(boolean force) {
    //force表示是否需要强制更新的意思

    boolean logOutlier = false;
    long dischargeDuration = 0;

    //判断当前电量是否危险,mBatteryProps包含了电池的所有信息,由底层传递过来,一旦小于等于5,就很危险了
    mBatteryLevelCritical = (mBatteryProps.batteryLevel <= mCriticalBatteryLevel);

    //得到当前的充电类型
    if (mBatteryProps.chargerUsbOnline) {
        //USB充电
        mPlugType = BatteryManager.BATTERY_PLUGGED_USB;
    } else if (mBatteryProps.chargerWirelessOnline) {
        //无限充电
        mPlugType = BatteryManager.BATTERY_PLUGGED_WIRELESS;
    } else if (mBatteryProps.chargerAcOnline) {
        //充电器充电
        mPlugType = BatteryManager.BATTERY_PLUGGED_AC;
    } else {
        //没有充电
        mPlugType = BATTERY_PLUGGED_NONE;
    }

    
    try {
        //电池属性改变的时候,会将全部信息写到mBatteryStats中,app端从mBatteryStats中获取当前电池的电量以及状态
        mBatteryStats.setBatteryState(mBatteryProps.batteryStatus, mBatteryProps.batteryHealth,
                mPlugType, mBatteryProps.batteryLevel, mBatteryProps.batteryTemperature,
                mBatteryProps.batteryVoltage, mBatteryProps.batteryChargeCounter,
                mBatteryProps.batteryFullCharge);
    } catch (RemoteException e) {
        // Should never happen.
    }

    //电池电量低(batteryLevel==0)且未充电时,弹出关机对话框
    shutdownIfNoPowerLocked();

    //电池温度过高(默认为68C),弹出关机对话框
    shutdownIfOverTempLocked();

    //强制更新,或电源相关属性发生变化时,进行对应操作
    if (force || (mBatteryProps.batteryStatus != mLastBatteryStatus ||
            mBatteryProps.batteryHealth != mLastBatteryHealth ||
            mBatteryProps.batteryPresent != mLastBatteryPresent ||
            mBatteryProps.batteryLevel != mLastBatteryLevel ||
            mPlugType != mLastPlugType ||
            mBatteryProps.batteryVoltage != mLastBatteryVoltage ||
            mBatteryProps.batteryTemperature != mLastBatteryTemperature ||
            mBatteryProps.maxChargingCurrent != mLastMaxChargingCurrent ||
            mBatteryProps.maxChargingVoltage != mLastMaxChargingVoltage ||
            mBatteryProps.batteryChargeCounter != mLastChargeCounter ||
            mInvalidCharger != mLastInvalidCharger)) {

        //充电状态发生变化
        if (mPlugType != mLastPlugType) {
            //上次是不充电状态,现在开始充电了,即由不充电变为充电状态
            if (mLastPlugType == BATTERY_PLUGGED_NONE) {
                // discharging -> charging

                // 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 != mBatteryProps.batteryLevel) {
                    dischargeDuration = SystemClock.elapsedRealtime() - mDischargeStartTime;
                    logOutlier = true;
                    EventLog.writeEvent(EventLogTags.BATTERY_DISCHARGE, dischargeDuration,
                            mDischargeStartLevel, mBatteryProps.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 = mBatteryProps.batteryLevel;
            }
        }

        //以下是记录电源的状态信息和电量信息
        if (mBatteryProps.batteryStatus != mLastBatteryStatus ||
                mBatteryProps.batteryHealth != mLastBatteryHealth ||
                mBatteryProps.batteryPresent != mLastBatteryPresent ||
                mPlugType != mLastPlugType) {
            EventLog.writeEvent(EventLogTags.BATTERY_STATUS,
                    mBatteryProps.batteryStatus, mBatteryProps.batteryHealth, mBatteryProps.batteryPresent ? 1 : 0,
                    mPlugType, mBatteryProps.batteryTechnology);
        }
        if (mBatteryProps.batteryLevel != mLastBatteryLevel) {
            // Don't do this just from voltage or temperature changes, that is
            // too noisy.
            EventLog.writeEvent(EventLogTags.BATTERY_LEVEL,
                    mBatteryProps.batteryLevel, mBatteryProps.batteryVoltage, mBatteryProps.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?
            // 当前未充电,且当前电量小于提醒电量,设置低电量为true
            if (mPlugType == BATTERY_PLUGGED_NONE
                    && mBatteryProps.batteryLevel <= mLowBatteryWarningLevel) {
                mBatteryLevelLow = true;
            }
        } else {
            // Should we now switch out of low battery mode?
            if (mPlugType != BATTERY_PLUGGED_NONE) {
                //开始充电了,退出低电量模式
                mBatteryLevelLow = false;
            } else if (mBatteryProps.batteryLevel >= mLowBatteryCloseWarningLevel) {
                //电池电量充足,退出低电量模式
                mBatteryLevelLow = false;
            } else if (force && mBatteryProps.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.

        //以下是单独发送一些广播信息,通知外界电池属性的变化:
        //通知进入充电状态,或离开充电状态
        //通知电源进入低电模式,或离开低电模式
        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);
                }
            });
        } 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 &&
                mBatteryProps.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);
                }
            });
        }
        
        //发送广播ACTION_BATTERY_CHANGED给外界,内含电源当前的全部信息
        sendIntentLocked();

        // 根据电源的电量和状态,改变LED灯的颜色
        mLed.updateLightsLocked();

        if (logOutlier && dischargeDuration != 0) {
            //利用BatteryInfoService记录耗电情况的dump文件
            logOutlierLocked(dischargeDuration);
        }

        //更新本地变量
        mLastBatteryStatus = mBatteryProps.batteryStatus;
        mLastBatteryHealth = mBatteryProps.batteryHealth;
        mLastBatteryPresent = mBatteryProps.batteryPresent;
        mLastBatteryLevel = mBatteryProps.batteryLevel;
        mLastPlugType = mPlugType;
        mLastBatteryVoltage = mBatteryProps.batteryVoltage;
        mLastBatteryTemperature = mBatteryProps.batteryTemperature;
        mLastMaxChargingCurrent = mBatteryProps.maxChargingCurrent;
        mLastMaxChargingVoltage = mBatteryProps.maxChargingVoltage;
        mLastChargeCounter = mBatteryProps.batteryChargeCounter;
        mLastBatteryLevelCritical = mBatteryLevelCritical;
        mLastInvalidCharger = mInvalidCharger;
    }
}

 

3.再来看下updateBatteryWarningLevelLocked()函数:

private void updateBatteryWarningLevelLocked() {
    final ContentResolver resolver = mContext.getContentResolver();

    //获取XML中配置的默认警告电量值
    int defWarnLevel = mContext.getResources().getInteger(
            com.android.internal.R.integer.config_lowBatteryWarningLevel);

    //获取设置中用户定义的电量警告值
    mLowBatteryWarningLevel = Settings.Global.getInt(resolver,
            Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, defWarnLevel);

    //用户没有定义,则使用默认的
    if (mLowBatteryWarningLevel == 0) {
        mLowBatteryWarningLevel = defWarnLevel;
    }

    //警告值不能低于危险值
    if (mLowBatteryWarningLevel < mCriticalBatteryLevel) {
        mLowBatteryWarningLevel = mCriticalBatteryLevel;
    }

    //计算出关闭警告的电量值
    mLowBatteryCloseWarningLevel = mLowBatteryWarningLevel + mContext.getResources().getInteger(
            com.android.internal.R.integer.config_lowBatteryCloseWarningBump);

    //更新电池信息
    processValuesLocked(true);
}

 

4.看下充电时的led灯变化

日常开发中,关于充电时的led灯的bug比较多,这里简单的说下,Led是BatteryService的内部类:

private final class Led {
    private final Light mBatteryLight;

    private final int mBatteryLowARGB;
    private final int mBatteryMediumARGB;
    private final int mBatteryFullARGB;
    private final int mBatteryLedOn;
    private final int mBatteryLedOff;

    public Led(Context context, LightsManager lights) {
        //获取充电led灯,这个在LightService中说明过
        mBatteryLight = lights.getLight(LightsManager.LIGHT_ID_BATTERY);

        //以下是不同电量状态时的led灯颜色
        mBatteryLowARGB = context.getResources().getInteger(
                com.android.internal.R.integer.config_notificationsBatteryLowARGB);
        mBatteryMediumARGB = context.getResources().getInteger(
                com.android.internal.R.integer.config_notificationsBatteryMediumARGB);
        mBatteryFullARGB = context.getResources().getInteger(
                com.android.internal.R.integer.config_notificationsBatteryFullARGB);
        mBatteryLedOn = context.getResources().getInteger(
                com.android.internal.R.integer.config_notificationsBatteryLedOn);
        mBatteryLedOff = context.getResources().getInteger(
                com.android.internal.R.integer.config_notificationsBatteryLedOff);
    }

    //更新led灯
    public void updateLightsLocked() {
        final int level = mBatteryProps.batteryLevel;
        final int status = mBatteryProps.batteryStatus;
        //电量低于BatteryWarning的电量
        if (level < mLowBatteryWarningLevel) {
            //已经连接上充电
            if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
                //指示灯显示红色
                mBatteryLight.setColor(mBatteryLowARGB);
            } else {
                //没有连接充电线,红灯闪烁
                mBatteryLight.setFlashing(mBatteryLowARGB, Light.LIGHT_FLASH_TIMED,
                        mBatteryLedOn, mBatteryLedOff);
            }
        } else if (status == BatteryManager.BATTERY_STATUS_CHARGING
                || status == BatteryManager.BATTERY_STATUS_FULL) {
            //当前正在充电或者当前电量已经充满
            if (status == BatteryManager.BATTERY_STATUS_FULL || level >= 90) {
                //当电池已经充满或者快要充满的时候指示灯显示绿色
                mBatteryLight.setColor(mBatteryFullARGB);
            } else {
                //当正在充电或者电量超过一半的时候,指示灯显示橘黄色
                mBatteryLight.setColor(mBatteryMediumARGB);
            }
        } else {
            //其他正常情况指示灯关闭
            mBatteryLight.turnOff();
        }
    }
}

 

5.电池属性改变的时候,为什么要将全部信息传递到mBatteryStats中?

IBatteryStats mBatteryStats = BatteryStatsService.getService();

IBatteryStats的实现类是BatteryStatsImpl,BatteryStatsImpl继承自BatteryStats,BatteryStats可以被app获取到,它的setBatteryState()方法会将电池属性写到历史记录,并且计算了电池的已用时间和可用时间,这样,当我们需要用到电池信息的时候,就可以先获取到当前的BatteryStats,然后再从BatteryStats中获取电池属性;

从上面可以看出,app端既可以从广播得到电池的当前属性,还可以通过BatteryStats得到电池的当前属性;

 

 类似资料: