Android5.1 电池充电剩余时间计算

  • Post author:
  • Post category:其他


android5.1手机在充电的时候,并且在锁屏界面的时候会显示还剩多少时间电池充满电。我们就这个机制进行下深入分析:

首先对电池的变化都会监听BatteryService发出的Intent.ACTION_BATTERY_CHANGED广播,因此在framework目录下全局搜索,结果发现在./base/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java这个目录下,也就是keyguard中有对这个广播的监控

在KeyguardUpdateMonitor.java这个文件中

    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {

        public void onReceive(Context context, Intent intent) {
            final String action = intent.getAction();
            if (DEBUG) Log.d(TAG, "received broadcast " + action);

            if (Intent.ACTION_TIME_TICK.equals(action)
                    || Intent.ACTION_TIME_CHANGED.equals(action)
                    || Intent.ACTION_TIMEZONE_CHANGED.equals(action)) {
                mHandler.sendEmptyMessage(MSG_TIME_UPDATE);
            } else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {//监听电池变化的广播
                final int status = intent.getIntExtra(EXTRA_STATUS, BATTERY_STATUS_UNKNOWN);
                final int plugged = intent.getIntExtra(EXTRA_PLUGGED, 0);
                final int level = intent.getIntExtra(EXTRA_LEVEL, 0);
                final int health = intent.getIntExtra(EXTRA_HEALTH, BATTERY_HEALTH_UNKNOWN);
                final Message msg = mHandler.obtainMessage(
                        MSG_BATTERY_UPDATE, new BatteryStatus(status, level, plugged, health));
                mHandler.sendMessage(msg);//发消息
            }

接下来我们搜索下MSG_BATTERY_UPDATE这个消息,

    private void handleBatteryUpdate(BatteryStatus status) {
        if (DEBUG) Log.d(TAG, "handleBatteryUpdate");
        final boolean batteryUpdateInteresting = isBatteryUpdateInteresting(mBatteryStatus, status);
        mBatteryStatus = status;
        if (batteryUpdateInteresting) {
            for (int i = 0; i < mCallbacks.size(); i++) {
                KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
                if (cb != null) {
                    cb.onRefreshBatteryInfo(status);//接下来我们就搜一下谁定义了onRefreshBatteryInfo这个接口
                }
            }
        }
    }

结果在KeyguardIndicationController.java这个文件中

    KeyguardUpdateMonitorCallback mUpdateMonitor = new KeyguardUpdateMonitorCallback() {
        @Override
        public void onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status) {
            boolean isChargingOrFull = status.status == BatteryManager.BATTERY_STATUS_CHARGING
                    || status.status == BatteryManager.BATTERY_STATUS_FULL;
            mPowerPluggedIn = status.isPluggedIn() && isChargingOrFull;
            mPowerCharged = status.isCharged();
            updateIndication();
        }
    };

接下来看下updateIndication

    private void updateIndication() {
        if (mVisible) {
            mTextView.switchIndication(computeIndication());
        }
    }

接下来就是看下如何计算电池剩余时间的

    private String computeIndication() {
        if (!TextUtils.isEmpty(mTransientIndication)) {
            return mTransientIndication;
        }
        if (mPowerPluggedIn) {//当在充电时
            return computePowerIndication();
        }
        return mRestingIndication;
    }

    private String computePowerIndication() {
        if (mPowerCharged) {
            return mContext.getResources().getString(R.string.keyguard_charged);
        }

        // Try fetching charging time from battery stats.
        try {//剩余时间通过BatteryStatsService获取
            long chargingTimeRemaining = mBatteryInfo.computeChargeTimeRemaining();
            if (chargingTimeRemaining > 0) {
                String chargingTimeFormatted = Formatter.formatShortElapsedTimeRoundingUpToMinutes(
                        mContext, chargingTimeRemaining);
                return mContext.getResources().getString(
                        R.string.keyguard_indication_charging_time, chargingTimeFormatted);
            }
        } catch (RemoteException e) {
            Log.e(TAG, "Error calling IBatteryStats: ", e);
        }

        // Fall back to simple charging label.
        return mContext.getResources().getString(R.string.keyguard_plugged_in);
    }

下面是mBatteryInfo的赋值

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


然后看BatteryStatsService中的computeChargeTimeRemaining代码

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

其中mStats是在BatteryStatsService构造的时候新建的:

    BatteryStatsService(File systemDir, Handler handler) {
        mStats = new BatteryStatsImpl(systemDir, handler);
    }


最后我们就来分析下BatteryStatsImpl中的computeChargeTimeRemaining函数:

    @Override
    public long computeChargeTimeRemaining(long curTime) {
        if (mOnBattery) {//当处于用电池的状态直接退出
            // Not yet working.
            Slog.e(TAG, "mOnBattery");
            return -1;
        }
        /* Broken
        int curLevel = mCurrentBatteryLevel;
        int plugLevel = mDischargePlugLevel;
        if (plugLevel < 0 || curLevel < (plugLevel+1)) {
            return -1;
        }
        long duration = computeBatteryRealtime(curTime, STATS_SINCE_UNPLUGGED);
        if (duration < 1000*1000) {
            return -1;
        }
        long usPerLevel = duration/(curLevel-plugLevel);
        return usPerLevel * (100-curLevel);
        */
        if (mNumChargeStepDurations < 1) {
            Slog.e(TAG, "mNumChargeStepDurations");
            return -1;
        }//mChargeStepDurations是一个数组,保存着每一个level对应的时间,而mNumChargeStepDurations是现在总的一个level
        long msPerLevel = computeTimePerLevel(mChargeStepDurations, mNumChargeStepDurations);
        if (msPerLevel <= 0) {
            Slog.e(TAG, "msPerLevel");
            return -1;
        }
        Slog.e(TAG, "return time remianing" + (msPerLevel * (100-mCurrentBatteryLevel)) * 1000);
        return (msPerLevel * (100-mCurrentBatteryLevel)) * 1000;//因此msperlevel代表每一个level需要多少时间,100-mCurrentBatteryLevel代表需要充电的电量
    }


再来看看

    private long computeTimePerLevel(long[] steps, int numSteps) {
        // 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++) {
            total += steps[i] & STEP_LEVEL_TIME_MASK;//高位保存的别的信息,需要将它去除
        }
        return total / numSteps;//所有的level的时间和除以总的level,就是每个level的平均时间

但是现在的关键mChargeStepDurations,和mNumChargeStepDurations这两个成员变量如何获得。

当在BatteryService更新电池信息的时候,如下:会调用BatteryStatsService的setBatteryState函数

        try {
            mBatteryStats.setBatteryState(mBatteryProps.batteryStatus, mBatteryProps.batteryHealth,
                    mPlugType, mBatteryProps.batteryLevel, mBatteryProps.batteryTemperature,
                    mBatteryProps.batteryVoltage);
        } catch (RemoteException e) {
            // Should never happen.
        }

而在BatteryStatsService的setBatteryState函数中:检查下权限然后调用了BatteryStatsImpl的setBatteryState函数

    public void setBatteryState(int status, int health, int plugType, int level,
            int temp, int volt) {
        enforceCallingPermission();
        mStats.setBatteryState(status, health, plugType, level, temp, volt);
    }

接下来我们来看下核心函数:

    public void setBatteryState(int status, int health, int plugType, int level,
            int temp, int volt) {
        synchronized(this) {
            final boolean onBattery = plugType == BATTERY_PLUGGED_NONE;
            final long uptime = SystemClock.uptimeMillis();
            final long elapsedRealtime = SystemClock.elapsedRealtime();
            int oldStatus = mHistoryCur.batteryStatus;
            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().
                if (onBattery == mOnBattery) {
                    if (onBattery) {
                        mHistoryCur.states &= ~HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
                    } else {
                        mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
                    }
                }
                oldStatus = status;
            }
            if (onBattery) {
                mDischargeCurrentLevel = level;
                if (!mRecordingHistory) {
                    mRecordingHistory = true;
                    startRecordingHistory(elapsedRealtime, uptime, true);
                }
            } else if (level < 96) {
                if (!mRecordingHistory) {
                    mRecordingHistory = true;
                    startRecordingHistory(elapsedRealtime, uptime, true);
                }
            }
            mCurrentBatteryLevel = level;
            if (mDischargePlugLevel < 0) {
                mDischargePlugLevel = level;
            }
            if (onBattery != mOnBattery) {
                mHistoryCur.batteryLevel = (byte)level;
                mHistoryCur.batteryStatus = (byte)status;
                mHistoryCur.batteryHealth = (byte)health;
                mHistoryCur.batteryPlugType = (byte)plugType;
                mHistoryCur.batteryTemperature = (short)temp;
                mHistoryCur.batteryVoltage = (char)volt;
                setOnBatteryLocked(elapsedRealtime, uptime, onBattery, oldStatus, level);
            } else {
                boolean changed = false;
                if (mHistoryCur.batteryLevel != level) {
                    mHistoryCur.batteryLevel = (byte)level;
                    changed = true;
                }
                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;
                }
                if (temp >= (mHistoryCur.batteryTemperature+10)
                        || temp <= (mHistoryCur.batteryTemperature-10)) {
                    mHistoryCur.batteryTemperature = (short)temp;
                    changed = true;
                }
                if (volt > (mHistoryCur.batteryVoltage+20)
                        || volt < (mHistoryCur.batteryVoltage-20)) {
                    mHistoryCur.batteryVoltage = (char)volt;
                    changed = true;
                }
                if (changed) {
                    addHistoryRecordLocked(elapsedRealtime, uptime);
                }
                long modeBits = (((long)mInitStepMode) << STEP_LEVEL_INITIAL_MODE_SHIFT)
                        | (((long)mModStepMode) << STEP_LEVEL_MODIFIED_MODE_SHIFT)
                        | (((long)(level&0xff)) << STEP_LEVEL_LEVEL_SHIFT);//主要在高位存储一些模式与我们的关系不大
                if (onBattery) {
                    if (mLastDischargeStepLevel != level && mMinDischargeStepLevel > level) {
                        mNumDischargeStepDurations = addLevelSteps(mDischargeStepDurations,
                                mNumDischargeStepDurations, mLastDischargeStepTime,
                                mLastDischargeStepLevel - level, modeBits, elapsedRealtime);
                        mLastDischargeStepLevel = level;
                        mMinDischargeStepLevel = level;
                        mLastDischargeStepTime = elapsedRealtime;
                        mInitStepMode = mCurStepMode;
                        mModStepMode = 0;
                    }
                } else {
                    if (mLastChargeStepLevel != level && mMaxChargeStepLevel < level) {
                        mNumChargeStepDurations = addLevelSteps(mChargeStepDurations,
                                mNumChargeStepDurations, mLastChargeStepTime,
                                level - mLastChargeStepLevel, modeBits, elapsedRealtime);
                        mLastChargeStepLevel = level;
                        mMaxChargeStepLevel = level;
                        mLastChargeStepTime = elapsedRealtime;//将mLastChargeStepTime 赋成当前时间值
                        mInitStepMode = mCurStepMode;
                        mModStepMode = 0;
                    }
                }
            }
            if (!onBattery && status == BatteryManager.BATTERY_STATUS_FULL) {
                // We don't record history while we are plugged in and fully charged.
                // The next time we are unplugged, history will be cleared.
                mRecordingHistory = DEBUG;
            }
        }
    }

再对应下面的addLevelSteps函数,入参numStepLevels是level-mLastChargeStepLevel也就是代表这侧充了多少电,

    private static int addLevelSteps(long[] steps, int stepCount, long lastStepTime,
            int numStepLevels, long modeBits, long elapsedRealtime) {
        if (lastStepTime >= 0 && numStepLevels > 0) {
            long duration = elapsedRealtime - lastStepTime;//这次充电用了多少时间
            for (int i=0; i<numStepLevels; i++) {将计算每个电池level的消耗的时间
                System.arraycopy(steps, 0, steps, 1, steps.length-1);//这个函数的意思是将数组steps从位置0,长度steps.length-1,复制到位置1
                long thisDuration = duration / (numStepLevels-i);
                duration -= thisDuration;
                if (thisDuration > STEP_LEVEL_TIME_MASK) {
                    thisDuration = STEP_LEVEL_TIME_MASK;
                }
                steps[0] = thisDuration | modeBits;//这个是将一些模式信息存在高位,与我们不是很相关
            }
            stepCount += numStepLevels;
            if (stepCount > steps.length) {
                stepCount = steps.length;
            }
        }
        return stepCount;
    }

有可能比较难理解,我们直接举个例子

比如我们充了5格电,用了30s,当i=0,thisDuration = 30 / 5 = 6,step[0] = 6;当 i =1,thisDuration = 24 / (5 -1) = 6;然后将整个数组往后延,这样每次保存的值都会往后延。

最前面是最新的电池充电时间,mNumChargeStepDurations是所有的level,哪怕再次调用这个函数,数组还是往后延,该保存的值都保存了。

因此计算每格电池的平均充电时间,每格电池所花的时间之和除以就是所有的level即mNumChargeStepDurations。就是平均每格电池的充电时间。

至此BatteryStatsImpl中计算充电电池还剩多少时间我们已经分析完了。

接下来我们再来看下”batterystats.bin”这个文件

先来看它的读取:会在readSummaryFromParcel函数中,将成员变量都读出来。

    public void readLocked() {
        if (mFile == null) {
            Slog.w("BatteryStats", "readLocked: no file associated with this instance");
            return;
        }

        mUidStats.clear();

        try {
            File file = mFile.chooseForRead();
            if (!file.exists()) {
                return;
            }
            FileInputStream stream = new FileInputStream(file);

            byte[] raw = BatteryStatsHelper.readFully(stream);
            Parcel in = Parcel.obtain();
            in.unmarshall(raw, 0, raw.length);
            in.setDataPosition(0);
            stream.close();

            readSummaryFromParcel(in);
        } catch(Exception e) {
            Slog.e("BatteryStats", "Error reading battery statistics", e);
        }

readSummaryFromParcel函数,从”batterystats.bin”这个文件读取各个成员变量。

    public void readSummaryFromParcel(Parcel in) {
        final int version = in.readInt();
        if (version != VERSION) {
            Slog.w("BatteryStats", "readFromParcel: version got " + version
                + ", expected " + VERSION + "; erasing old stats");
            return;
        }

        readHistory(in, true);

        mStartCount = in.readInt();
        mUptime = in.readLong();
        mRealtime = in.readLong();
        mStartClockTime = in.readLong();
        mStartPlatformVersion = in.readString();
        mEndPlatformVersion = in.readString();
        mOnBatteryTimeBase.readSummaryFromParcel(in);
        mOnBatteryScreenOffTimeBase.readSummaryFromParcel(in);
        mDischargeUnplugLevel = in.readInt();
        mDischargePlugLevel = in.readInt();
        mDischargeCurrentLevel = in.readInt();
        mCurrentBatteryLevel = in.readInt();
        mLowDischargeAmountSinceCharge = in.readInt();
        mHighDischargeAmountSinceCharge = in.readInt();
        mDischargeAmountScreenOnSinceCharge = in.readInt();
        mDischargeAmountScreenOffSinceCharge = in.readInt();
        mNumDischargeStepDurations = in.readInt();
        in.readLongArray(mDischargeStepDurations);
        mNumChargeStepDurations = in.readInt();

然后还有两个写的函数,一个同步一个异步

    public void writeAsyncLocked() {
        writeLocked(false);
    }

    public void writeSyncLocked() {
        writeLocked(true);
    }

    void writeLocked(boolean sync) {
        if (mFile == null) {
            Slog.w("BatteryStats", "writeLocked: no file associated with this instance");
            return;
        }

        if (mShuttingDown) {
            return;
        }

        Parcel out = Parcel.obtain();
        writeSummaryToParcel(out, true);
        mLastWriteTime = SystemClock.elapsedRealtime();

        if (mPendingWrite != null) {
            mPendingWrite.recycle();
        }
        mPendingWrite = out;

        if (sync) {
            commitPendingDataToDisk();
        } else {
            BackgroundThread.getHandler().post(new Runnable() {
                @Override public void run() {
                    commitPendingDataToDisk();
                }
            });
        }
    }

在ActivityManagerService中新建BatteryStatsService的时候,会去调用BatteryStatsImpl的readLocked,读取”batterystats.bin”的值,来初始化自己的成员变量。

        mBatteryStatsService = new BatteryStatsService(systemDir, mHandler);
        mBatteryStatsService.getActiveStatistics().readLocked();
        mBatteryStatsService.getActiveStatistics().writeAsyncLocked();

比如在关机的时候进行写,但这时候是同步的。

我们怎么理解新建BatteryStatsService的时候,会去调用BatteryStatsImpl的readLocked,读取”batterystats.bin”的值,来初始化自己的成员变量。

比如说充电,一开机,之前的充电的每格电池的充电时间都没有,通过从”batterystats.bin”文件中读取,就可以将上次开机的状态知道了。

其实仔细看代码,知道插入usb充电时,由于mOnBattery 改变了会调用setOnBatteryLocked函数,而这时候将mLastChargeStepTime = -1;mNumChargeStepDurations = 0;

    void setOnBatteryLocked(final long mSecRealtime, final long mSecUptime, final boolean onBattery,
            final int oldStatus, final int level) {
        boolean doWrite = false;
        Message m = mHandler.obtainMessage(MSG_REPORT_POWER_CHANGE);
        m.arg1 = onBattery ? 1 : 0;
        mHandler.sendMessage(m);

        final long uptime = mSecUptime * 1000;
        final long realtime = mSecRealtime * 1000;
        final boolean screenOn = mScreenState == Display.STATE_ON;
        if (onBattery) {
           ...........
        } else {
            Slog.e("kangchen", "setOnBatteryLocked mNumChargeStepDurations = 0;");
            mOnBattery = mOnBatteryInternal = onBattery;
            pullPendingStateUpdatesLocked();
            mHistoryCur.batteryLevel = (byte)level;
            mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
            if (DEBUG_HISTORY) Slog.v(TAG, "Battery plugged to: "
                    + Integer.toHexString(mHistoryCur.states));
            addHistoryRecordLocked(mSecRealtime, mSecUptime);
            mDischargeCurrentLevel = mDischargePlugLevel = level;
            if (level < mDischargeUnplugLevel) {
                mLowDischargeAmountSinceCharge += mDischargeUnplugLevel-level-1;
                mHighDischargeAmountSinceCharge += mDischargeUnplugLevel-level;
            }
            updateDischargeScreenLevelsLocked(screenOn, screenOn);
            updateTimeBasesLocked(false, !screenOn, uptime, realtime);
            mNumChargeStepDurations = 0;
            mLastChargeStepLevel = level;
            mMaxChargeStepLevel = level;
            mLastChargeStepTime = -1;
            mInitStepMode = mCurStepMode;
            mModStepMode = 0;
        }
.............

下次BatteryService再次调用BatterStatsImpl的setBatteryState的时候,首先先要电量充了一格电才能进这个分支,但是由于这时候mLastChargeStepTime 还是-1,所以进addLevelSteps函数,直接返回,mNumChargeStepDurations 还是为0 。所以需要再充一格电才能进去,并且mNumChargeStepDurations 不为0

                } else {
                    if (mLastChargeStepLevel != level && mMaxChargeStepLevel < level) {
                        mNumChargeStepDurations = addLevelSteps(mChargeStepDurations,
                                mNumChargeStepDurations, mLastChargeStepTime,
                                level - mLastChargeStepLevel, modeBits, elapsedRealtime);
                        mLastChargeStepLevel = level;
                        mMaxChargeStepLevel = level;
                        mLastChargeStepTime = elapsedRealtime;
                        mInitStepMode = mCurStepMode;
                        mModStepMode = 0;
                    }
                }

    private static int addLevelSteps(long[] steps, int stepCount, long lastStepTime,
            int numStepLevels, long modeBits, long elapsedRealtime) {
        if (lastStepTime >= 0 && numStepLevels > 0) {//lastStepTime为0 if进不去
            long duration = elapsedRealtime - lastStepTime;
            for (int i=0; i<numStepLevels; i++) {
                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;
                }
                steps[0] = thisDuration | modeBits;
            }
            stepCount += numStepLevels;
            if (stepCount > steps.length) {
                stepCount = steps.length;
            }
        }
        return stepCount;
    }

而且只有当mNumChargeStepDurations 大于0的时候,computeChargeTimeRemaining计算才会有正常返回值。

    public long computeChargeTimeRemaining(long curTime) {
        if (mOnBattery) {
            // Not yet working.
            return -1;
        }
        /* Broken
        int curLevel = mCurrentBatteryLevel;
        int plugLevel = mDischargePlugLevel;
        if (plugLevel < 0 || curLevel < (plugLevel+1)) {
            return -1;
        }
        long duration = computeBatteryRealtime(curTime, STATS_SINCE_UNPLUGGED);
        if (duration < 1000*1000) {
            return -1;
        }
        long usPerLevel = duration/(curLevel-plugLevel);
        return usPerLevel * (100-curLevel);
        */
        if (mNumChargeStepDurations < 1) {
            return -1;
        }
        long msPerLevel = computeTimePerLevel(mChargeStepDurations, mNumChargeStepDurations);
        if (msPerLevel <= 0) {
            return -1;
        }
        return (msPerLevel * (100-mCurrentBatteryLevel)) * 1000;
    }

因此插上usb线,在锁屏状态下。需要充两格电或者以上,才会有剩余时间的显示。






版权声明:本文为kc58236582原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。