[Android Framework] 8.1 Battery系列(四) 电量还需多长时间充满时间计算

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/FightFightFight/article/details/82467246

概述

当设备插入充电且电量发生变化一段时间后,在Settings->Battery中和锁屏界面都会有”还需多长时间充满”提示,这里来分析下这个时长是如何获得的。

Settings中调用接口:

packages/apps/Settings/src/com/android/settings/fuelgauge/BatteryInfo.java

Final long chargeTime = stats.computeChargeTimeRemaining(elapsedRealtimeUs);

KeyGuard中调用接口:

frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java

mBatteryInfo = IBatteryStats.Stub.asInterface(
        ServiceManager.getService(BatteryStats.SERVICE_NAME));
chargingTimeRemaining = mBatteryInfo.computeChargeTimeRemaining();

以上两种方式中,KeyGuard通过获得BatteryStatsService对象开始调用方法,Settings中则通过BatteryStatsImpl对象调用。但最终都调用的是BatteryStatsImpl中的computeChargeTimeRemaining(long)方法,先看BatteryStatsService中的computeChargeTimeRemaining()方法:

frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java

public long computeChargeTimeRemaining() {
    synchronized (mStats) {
        long time = mStats.computeChargeTimeRemaining(SystemClock.elapsedRealtime());
        return time >= 0 ? (time/1000) : time;
    }
}

在这个方法中,获取了当前系统运行时间后,作为参数调用BatterStatsImpl中的方法,得到计算值并返回,因此,这个方法就是核心方法。该方法如下:

@Override
public long computeChargeTimeRemaining(long curTime) {
    Slog.d(TAG,"computeChargeTimeRemaining()---start,mOnBattery="+mOnBattery);
   //放电情况下直接返回-1 
   if (mOnBattery) {
        // Not yet working.
        return -1;
    //mChargeStepTracker是充电记录器
    //mNumStepDurations表示充电量的步数纸盒,每次开始充电,为0,之后每充一个电,该值加1
    if (mChargeStepTracker.mNumStepDurations < 1) {
        return -1;
    }
    //获取充一个电量的时间
    long msPerLevel = mChargeStepTracker.computeTimePerLevel();
    if (msPerLevel <= 0) {
        return -1;
    }
    //充一个电的时间*距离充满还有多少电得到预估的时间
    return (msPerLevel * (100-mCurrentBatteryLevel)) * 1000;
}

在这个方法中,首先计算充一个电所需的时长,然后通过这个时长x充满所需多少电得到总时长返回。

整个逻辑思路非常简单,但是其中的算法和逻辑相对来说比较难以理解,尤其是mChargeStepTracker这个对象是做什么的。因此,这里在分析是如何计算充一个电所需时长之前,先缕清楚电池信息是如何流转到BatteryStateImpl中的,这个流程搞清楚之后,之后的逻辑就不那么吃力了。

首先来看看上述方法中的mChargeStepTracker对象,它是LevelStepTracker类的一个实例,从名称来看就知道是负责电量等级跟踪的,这里先看下它的三个属性和构造方法:

public static final class LevelStepTracker {
    public long mLastStepTime = -1;//上次充了一个电时的时间
    public int mNumStepDurations;//充一个电的步数和,如电量由1充到2时该值为1,由2冲到3时该值为2,...
    public final long[] mStepDurations;//充一个电所用时长的数组

    public LevelStepTracker(int maxLevelSteps) {
        mStepDurations = new long[maxLevelSteps];
    }

    public LevelStepTracker(int numSteps, long[] steps) {
        mNumStepDurations = numSteps;
        mStepDurations = new long[numSteps];
        System.arraycopy(steps, 0, mStepDurations, 0, numSteps);
    }

mChargeStepTracker对象的初始化如下:

//实例化mChargeStepTracker
final LevelStepTracker mChargeStepTracker = new LevelStepTracker(MAX_LEVEL_STEPS);//200

还有一个重要方法,会在稍后进行分析。
接下来看看computeChargeTimeRemaining()中所需值的来源。

我们知道,在Framework层中和电池相关的有两个服务类,BatteryService是System服务之一,负责监听电池数据,它会获取由healthd上报的电池信息。BatteryStatsService则由AMS中启动,负责统计电池使用数据。当BatteryService中获取到新的电池数据时,将会通过setBatteryState()方法通知给BatteryStatsService以进行统计,因此,我们就从这个方法入手,看看当有电池数据上报时,它是如何处理的。

首先来看BatteryService中接收healthd中的上报信息后,通过setBatteryState()方法将电池数据传送给BatteryStatsService:

/frameworks/base/services/core/java/com/android/server/BatteryService.java

// Let the battery stats keep track of the current level.
try {
    mBatteryStats.setBatteryState(mBatteryProps.batteryStatus, mBatteryProps.batteryHealth,
            mPlugType, mBatteryProps.batteryLevel, mBatteryProps.batteryTemperature,
            mBatteryProps.batteryVoltage, mBatteryProps.batteryChargeCounter,
            mBatteryProps.batteryFullCharge);
} catch (RemoteException e) {
    // Should never happen.
}

而BatteryStatsService中的setBatteryState()方法又调用了BatteryStatsImpl的setBatteryStateLocked()方法,并在BatteryStatsImpl中进行最终的处理。setBatteryStateLocked()方法比较庞大,详细的解释都在注释中,代码如下:

public void setBatteryStateLocked(int status, int health, int plugType, int level,
        int temp, int volt, int chargeUAh, int chargeFullUAh) {
    //温度没有带符号位,如果存在负值,一律按0处理
    temp = Math.max(0, temp);
    //是否插有充电器,true表示没有插入任何充电器
    final boolean onBattery = plugType == BATTERY_PLUGGED_NONE;
    //获取当前系统时间
    final long uptime = mClocks.uptimeMillis();
    final long elapsedRealtime = mClocks.elapsedRealtime();
    //开机第一次该值为false,之后恒为true
    //因此此处做一些开机后的赋值,这些赋值将会在之后的逻辑中被覆盖
    if (!mHaveBatteryLevel) {
        mHaveBatteryLevel = true;
        // We start out assuming that the device is plugged in (not
        // on battery).  If our first report is now that we are indeed
        // plugged in, then twiddle our state to correctly reflect that
        // since we won't be going through the full setOnBattery().
        //插入充电器时为false,不插入充电器时为true
        if (onBattery == mOnBattery) {
            //进行置位操作
            if (onBattery) {
                //未插入充电器,移除标志
                mHistoryCur.states &= ~HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
            } else {
                //插入充电器,设置标志
                mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
            }
        }
        // Always start out assuming charging, that will be updated later.
        //第一次进入时,假设当前处于充电状态,设置一个标志
        mHistoryCur.states2 |= HistoryItem.STATE2_CHARGING_FLAG;
        mHistoryCur.batteryStatus = (byte)status;//电池状态
        mHistoryCur.batteryLevel = (byte)level;//电量等级
        mHistoryCur.batteryChargeUAh = chargeUAh;//当前电量
        //初始化在系统运行期间,所充的最大电量值、最小电量值、上一次充电的电量值
        //如从23充电至50,则以上三值将分别为23,50,50.
        mMaxChargeStepLevel = mMinDischargeStepLevel =
                mLastChargeStepLevel = mLastDischargeStepLevel = level;
        mLastChargingStateLevel = level;
        //当前电量不等于新上报电量值 || 是否插入充电器有发生改变
    } else if (mCurrentBatteryLevel != level || mOnBattery != onBattery) {
        //如果充满电且未插入充电线,记录DailyItem
        recordDailyStatsIfNeededLocked(level >= 100 && onBattery);
    }
    //将原来电池状态值保存在局部变量中
    int oldStatus = mHistoryCur.batteryStatus;
    if (onBattery) {//没有插入充电器,也即现在开始要放电了,标记当前电量为放电时电量
        mDischargeCurrentLevel = level;
        //记录在历史数据中
        if (!mRecordingHistory) {
            mRecordingHistory = true;
            startRecordingHistory(elapsedRealtime, uptime, true);
        }
    } else if (level < 96) {//电量值小于96时
        if (!mRecordingHistory) {
            mRecordingHistory = true;
            //记录在历史数据中
            startRecordingHistory(elapsedRealtime, uptime, true);
        }
    }
    //将level设置为当前电池电量
    mCurrentBatteryLevel = level;
    //初始化,该值表示放电中时插入充电时刻的电量
    if (mDischargePlugLevel < 0) {
        mDischargePlugLevel = level;
    }
    //"是否插入充电器"发生了改变
    if (onBattery != mOnBattery) {
        //将当前电池信息设置到mHistoryCur中
        mHistoryCur.batteryLevel = (byte)level;
        mHistoryCur.batteryStatus = (byte)status;
        mHistoryCur.batteryHealth = (byte)health;
        mHistoryCur.batteryPlugType = (byte)plugType;
        mHistoryCur.batteryTemperature = (short)temp;
        mHistoryCur.batteryVoltage = (char)volt;
        //电池已充UAh数,如果小于上次记录值,说明在放电
        if (chargeUAh < mHistoryCur.batteryChargeUAh) {
            // Only record discharges
            //获取消耗的电量
            final long chargeDiff = mHistoryCur.batteryChargeUAh - chargeUAh;
            //累加消耗了多少电量,mDischargeCounter是LongSamplingCounter的一个实例,用来统计放电总量
            mDischargeCounter.addCountLocked(chargeDiff);
            //累加在灭屏状态下消耗了多少电量
            mDischargeScreenOffCounter.addCountLocked(chargeDiff);
            if (isScreenDoze(mScreenState)) {
                //累加在Doze状态下消耗了多少电量,Doze状态下也处于灭屏状态,但cpu未休眠
                mDischargeScreenDozeCounter.addCountLocked(chargeDiff);
            }
        }
        //将已充电量(UAh为单位)赋值给mHistoryCur属性值
        mHistoryCur.batteryChargeUAh = chargeUAh;
        //用来设置OnBattery相关
        setOnBatteryLocked(elapsedRealtime, uptime, onBattery, oldStatus, level, chargeUAh);
    } else {//"是否插入充电器"没有发生改变
        boolean changed = false;
        //电量发生改变
        if (mHistoryCur.batteryLevel != level) {
            mHistoryCur.batteryLevel = (byte)level;
            changed = true;
            // TODO(adamlesinski): Schedule the creation of a HistoryStepDetails record
            // which will pull external stats.
            //开始拉取外部设备(Wifi、BT、modem)的电池信息
            scheduleSyncExternalStatsLocked("battery-level", ExternalStatsSync.UPDATE_ALL);
        }
        //电池状态发生改变
        if (mHistoryCur.batteryStatus != status) {
            mHistoryCur.batteryStatus = (byte)status;
            changed = true;
        }
        //电池健康状态发生改变
        if (mHistoryCur.batteryHealth != health) {
            mHistoryCur.batteryHealth = (byte)health;
            changed = true;
        }
        //充电类型发生改变
        if (mHistoryCur.batteryPlugType != plugType) {
            mHistoryCur.batteryPlugType = (byte)plugType;
            changed = true;
        }
        //电池温度升高10度或降低10度
        if (temp >= (mHistoryCur.batteryTemperature+10)
                || temp <= (mHistoryCur.batteryTemperature-10)) {
            mHistoryCur.batteryTemperature = (short)temp;
            changed = true;
        }
        //充电电压升高或者降低20v
        if (volt > (mHistoryCur.batteryVoltage+20)
                || volt < (mHistoryCur.batteryVoltage-20)) {
            mHistoryCur.batteryVoltage = (char)volt;
            changed = true;
        }
        //已充电数升高或者降低10mAh
        if (chargeUAh >= (mHistoryCur.batteryChargeUAh+10)
                || chargeUAh <= (mHistoryCur.batteryChargeUAh-10)) {
            if (chargeUAh < mHistoryCur.batteryChargeUAh) {
                // Only record discharges
                final long chargeDiff = mHistoryCur.batteryChargeUAh - chargeUAh;
                mDischargeCounter.addCountLocked(chargeDiff);
                mDischargeScreenOffCounter.addCountLocked(chargeDiff);
                if (isScreenDoze(mScreenState)) {
                    mDischargeScreenDozeCounter.addCountLocked(chargeDiff);
                }
            }
            mHistoryCur.batteryChargeUAh = chargeUAh;
            changed = true;
        }
        //modeBits是一个标志位,long类型共64bit
        long modeBits = (((long)mInitStepMode) << STEP_LEVEL_INITIAL_MODE_SHIFT)//64-57位存储mInitStepMode
                | (((long)mModStepMode) << STEP_LEVEL_MODIFIED_MODE_SHIFT)//56-49存储mModStepMode
                | (((long)(level&0xff)) << STEP_LEVEL_LEVEL_SHIFT);//48-40存储当前电量

        //没有插入充电器,即放电
        if (onBattery) {
            //在这个方法中会根据是否charging改变发送BatteryManager.ACTION_CHARGING/DISCHARGING广播
            changed |= setChargingLocked(false);
            //上次放电时的电量!=当前新电量&&放电过程中最小电量>当前新电量
            if (mLastDischargeStepLevel != level && mMinDischargeStepLevel > level) {
                //使用放电跟踪器记录放电时电量步数
                mDischargeStepTracker.addLevelSteps(mLastDischargeStepLevel - level,
                        modeBits, elapsedRealtime);
                //使用放电跟踪器记录放电时电量步数
                mDailyDischargeStepTracker.addLevelSteps(mLastDischargeStepLevel - level,
                        modeBits, elapsedRealtime);
                mLastDischargeStepLevel = level;
                mMinDischargeStepLevel = level;
                mInitStepMode = mCurStepMode;
                mModStepMode = 0;
            }
        } else {//说明插有充电器
            if (level >= 90) {
                // If the battery level is at least 90%, always consider the device to be
                // charging even if it happens to go down a level.
                //如果电量大于等于90,则一律认为设备正在充电
                changed |= setChargingLocked(true);
                //上次充电时电量
                mLastChargeStepLevel = level;
            } if (!mCharging) {//没有进行充电
                if (mLastChargeStepLevel < level) {
                    // We have not reporting that we are charging, but the level has now
                    // gone up, so consider the state to be charging.
                    //设置为放电
                    changed |= setChargingLocked(true);
                    mLastChargeStepLevel = level;
                }
            } else {
                if (mLastChargeStepLevel > level) {
                    //如果上次充电时电量大于当前level,说明是没有进行充电
                    changed |= setChargingLocked(false);
                    mLastChargeStepLevel = level;
                }
            }
            //这三个值不等,则说明没有进入以上if-else中,一般在充电且小于90时,每充一个level
            //的电都会进入以下方法,进行记录
            if (mLastChargeStepLevel != level && mMaxChargeStepLevel < level) {
                //使用充电跟踪器记录充电时电量步数,这是计算电量充满时间的关机
                mChargeStepTracker.addLevelSteps(level - mLastChargeStepLevel,
                        modeBits, elapsedRealtime);
                //每日充电跟踪器记录充电时电量步数
                mDailyChargeStepTracker.addLevelSteps(level - mLastChargeStepLevel,
                        modeBits, elapsedRealtime);
                //上次充一个电时的电量
                mLastChargeStepLevel = level;
                mMaxChargeStepLevel = level;
                mInitStepMode = mCurStepMode;
                mModStepMode = 0;
            }
        }
        if (changed) {//如果电池状态发生改变
            //添加历史记录
            addHistoryRecordLocked(elapsedRealtime, uptime);
        }
    }
    //如果插入充电器且电池状态值为充满状态,说明此时点已经充满
    if (!onBattery && status == BatteryManager.BATTERY_STATUS_FULL) {
        mRecordingHistory = DEBUG;
    }
    // ...........
}

这个方法可以说是非常大了,其中这个方法中还调用了如setOnBatteryLocked()等方法,在这篇文章中就先不进行分析了。

在以上方法中,针对于计算还需多久充满这个场景,需要清楚以下一个对象及属性即可:

  • 1.onBattery:该值表示是否插有充电装置(USB,AC等),没有插入时为true,因此充电时该值为false。
  • 2.mChargeStepTracker:LevelStepTracker类的一个实例,用来记录充电步数的一个跟踪器,计算时间时通过它记录的数据实现。
  • 3.mChargeStepTracker.addLevelSteps():每充一格电,都会使用这个方法记录充电时长和步数。

在这个方法中,有如下一句:

mChargeStepTracker.addLevelSteps(level - mLastChargeStepLevel,
        modeBits, elapsedRealtime);

这里正是计算电池充满时间的关键方法,现在就来看看当有新的电量值时,mChargeStepTracker.addLevelSteps()中做了什么:


public void addLevelSteps(int numStepLevels, long modeBits, long elapsedRealtime) {
    //暂存mNumStepDurations值,这个值已说过,表示充电量的步数和,每次开始充电,为0,之后每充一个电,该值加1
    int stepCount = mNumStepDurations;
    //暂存上次充一个电时刻的时间
    final long lastStepTime = mLastStepTime;
    //上次充满一个电时刻的时间>=0 && 当前电量-上次充电电量>0
    if (lastStepTime >= 0 && numStepLevels > 0) {
        //暂存每充一个电所需时长的数组
        final long[] steps = mStepDurations;
        //得到上次和这次的时长,即每充一个电量的时长
        long duration = elapsedRealtime - lastStepTime;
        //numStepLevels是每充一个电时的步数,所以是1,如12->13,numStepLevels=13-12=1
        for (int i=0; i<numStepLevels; i++) {
            //数组每次往后移动一位,会将新值写到step[0]
            System.arraycopy(steps, 0, steps, 1, steps.length-1);
            //时长除以步长,每充一个电的时长
            long thisDuration = duration / (numStepLevels-i);
            duration -= thisDuration;
            if (thisDuration > STEP_LEVEL_TIME_MASK) {
                thisDuration = STEP_LEVEL_TIME_MASK;
            }
            //将时长和modeBits信息保存在数据第一个元素,同时modeBits的值为高位41-64位的值
            //在获取时长时,将通过steps[i] & STEP_LEVEL_TIME_MASK将高48位清0
            steps[0] = thisDuration | modeBits;
        }
        stepCount += numStepLevels;//每次将充一个电时的步数累加
        if (stepCount > steps.length) {
            stepCount = steps.length;
        }
    }
    //得到累加后新的步数
    mNumStepDurations = stepCount;
    //标记上次充一个电的时间
    mLastStepTime = elapsedRealtime;
}

在这个方法中,每次充一格电,都将会得到mStepDurations数组和mNumStepDurationsmStepDurations[0]中保存的是每次充一格电所需的时间,mNumStepDurations则表示每次充电所经历的步数之和,每充一格电,该值将会加1.

现在,我们再回到直接计算电量充满时间的computeChargeTimeRemaining()方法中,就容易理解多了,从之前的代码中来看,其计算公式可以用如下公式来表示:

电量充满时间 = 充一格电所需的时间 x (100-当前电量)

充一格电所需的时间通过mChargeStepTracker.computeTimePerLevel()获取,我们看看这个方法:

public long computeTimePerLevel() {
    //充电步数数组
    final long[] steps = mStepDurations;
    //充电步数和
    final int numSteps = mNumStepDurations;
    // For now we'll do a simple average across all steps.
    //说明此时没有完成充一个电
    if (numSteps <= 0) {
        return -1;
    }
    long total = 0;
    for (int i=0; i<numSteps; i++) {
        //高位清零,得到实际时间,因为在计算时使用了step[0]=thisDuration | modeBits.
        total += steps[i] & STEP_LEVEL_TIME_MASK;//0x000000ffffffffffL
    }
    //获取一个每充一个电的平均时间值并返回
    return total / numSteps;
}

这个方法得到所有充电持续时间的平均值后返回给computeChargeTimeRemaining()方法,最终通过这个平均时间值乘以剩余需要的充电量从而得到预估时间。

猜你喜欢

转载自blog.csdn.net/FightFightFight/article/details/82467246