Android BatteryStatsHelper深入理解(and5.1)

来源:互联网 发布:在线电影下载软件 编辑:程序博客网 时间:2024/06/05 18:57

BatteryStatsHelper这个类主要是统计各个应用,多用户的每个用户,以及蓝牙,屏幕等耗电统计。

一般使用BatteryStatsHelper这个类,先要new一个实例,然后再调用create函数:

下面我们就先从create分析,两种create方法,其中sStatsXfer是静态的

    public void create(BatteryStats stats) {        mPowerProfile = new PowerProfile(mContext);        mStats = stats;    }    public void create(Bundle icicle) {        if (icicle != null) {            mStats = sStatsXfer;            mBatteryBroadcast = sBatteryBroadcastXfer;        }        mBatteryInfo = IBatteryStats.Stub.asInterface(                ServiceManager.getService(BatteryStats.SERVICE_NAME));        mPowerProfile = new PowerProfile(mContext);    }

mPowerProfile是从power_profile.xml读取的各个器件的电源消耗参数,文件如下:

<device name="Android">  <!-- Most values are the incremental current used by a feature,       in mA (measured at nominal voltage).       The default values are deliberately incorrect dummy values.       OEM's must measure and provide actual values before       shipping a device.       Example real-world values are given in comments, but they       are totally dependent on the platform and can vary       significantly, so should be measured on the shipping platform       with a power meter. -->  <item name="none">0</item>  <item name="screen.on">100</item>  <!-- ~200mA -->  <item name="screen.full">200</item>  <!-- ~300mA -->  <item name="bluetooth.active">90.5</item> <!-- Bluetooth data transfer, ~10mA -->  <item name="bluetooth.on">2.5</item>  <!-- Bluetooth on & connectable, but not connected, ~0.1mA -->  <item name="wifi.on">1.25</item>  <!-- ~3mA -->  <item name="wifi.active">130</item>  <!-- WIFI data transfer, ~200mA -->  <item name="wifi.scan">100</item>  <!-- WIFI network scanning, ~100mA -->  <item name="dsp.audio">30.5</item> <!-- ~10mA -->  <item name="dsp.video">72.5</item> <!-- ~50mA -->  <item name="radio.active">135</item> <!-- ~200mA -->  <item name="radio.scanning">5.3</item> <!-- cellular radio scanning for signal, ~10mA -->  <item name="gps.on">30</item> <!-- ~50mA -->  <!-- Current consumed by the radio at different signal strengths, when paging -->  <array name="radio.on"> <!-- Strength 0 to BINS-1 -->      <value>3.5</value> <!-- ~2mA -->      <value>2.4</value> <!-- ~1mA -->  </array>  <!-- Different CPU speeds as reported in       /sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state -->  <array name="cpu.speeds">      <value>624000</value> <!-- 624 MHz CPU speed -->      <value>699563</value> <!-- 699 MHz CPU speed -->      <value>799500</value> <!-- 799 MHz CPU speed -->      <value>899438</value> <!-- 899 MHz CPU speed -->      <value>999375</value> <!-- 999 MHz CPU speed -->      <value>1099313</value> <!-- 1099 MHz CPU speed -->      <value>1199250</value> <!-- 1199 MHz CPU speed -->      <value>1299188</value> <!-- 1299 MHz CPU speed -->      <value>1399125</value> <!-- 1399 MHz CPU speed -->      <value>1499063</value> <!-- 1499 MHz CPU speed -->      <value>1599000</value> <!-- 1599 MHz CPU speed -->  </array>  <!-- Current when CPU is idle -->  <item name="cpu.idle">2.2</item>  <!-- Current at each CPU speed, as per 'cpu.speeds' -->  <array name="cpu.active">//各个cpu频段的功耗      <value>54</value>      <value>63</value>      <value>72</value>      <value>80</value>      <value>90</value>      <value>100</value>      <value>109</value>      <value>115</value>      <value>121</value>      <value>127</value>      <value>135</value>  </array>  <!-- This is the battery capacity in mAh (measured at nominal voltage) -->  <item name="battery.capacity">2000</item></device>

接下来说下refreshStats是用来更新电池最新状态的,statsType是指充电状态还是非充电状态,asUsers指的是userId(多用户)

    public void refreshStats(int statsType, List<UserHandle> asUsers) {        final int n = asUsers.size();        SparseArray<UserHandle> users = new SparseArray<UserHandle>(n);        for (int i = 0; i < n; ++i) {            UserHandle userHandle = asUsers.get(i);            users.put(userHandle.getIdentifier(), userHandle);        }        refreshStats(statsType, users);    }    /**     * Refreshes the power usage list.     */    public void refreshStats(int statsType, SparseArray<UserHandle> asUsers) {        refreshStats(statsType, asUsers, SystemClock.elapsedRealtime() * 1000,                SystemClock.uptimeMillis() * 1000);    }

接下来分析下refreshStats函数

 public void refreshStats(int statsType, SparseArray<UserHandle> asUsers, long rawRealtimeUs,            long rawUptimeUs) {        // Initialize mStats if necessary.        getStats();        mMaxPower = 0;        mMaxRealPower = 0;        mComputedPower = 0;        mTotalPower = 0;        mWifiPower = 0;        mBluetoothPower = 0;        mAppMobileActive = 0;        mAppWifiRunning = 0;        mUsageList.clear();        mWifiSippers.clear();        mBluetoothSippers.clear();        mUserSippers.clear();        mUserPower.clear();        mMobilemsppList.clear();        if (mStats == null) {            return;        }        mStatsType = statsType;        mRawUptime = rawUptimeUs;        mRawRealtime = rawRealtimeUs;        mBatteryUptime = mStats.getBatteryUptime(rawUptimeUs);        mBatteryRealtime = mStats.getBatteryRealtime(rawRealtimeUs);        mTypeBatteryUptime = mStats.computeBatteryUptime(rawUptimeUs, mStatsType);        mTypeBatteryRealtime = mStats.computeBatteryRealtime(rawRealtimeUs, mStatsType);        mBatteryTimeRemaining = mStats.computeBatteryTimeRemaining(rawRealtimeUs);//获取电池剩余时间        mChargeTimeRemaining = mStats.computeChargeTimeRemaining(rawRealtimeUs);//获取充电剩余时间        if (DEBUG) {            Log.d(TAG, "Raw time: realtime=" + (rawRealtimeUs/1000) + " uptime="                    + (rawUptimeUs/1000));            Log.d(TAG, "Battery time: realtime=" + (mBatteryRealtime/1000) + " uptime="                    + (mBatteryUptime/1000));            Log.d(TAG, "Battery type time: realtime=" + (mTypeBatteryRealtime/1000) + " uptime="                    + (mTypeBatteryUptime/1000));        }        mMinDrainedPower = (mStats.getLowDischargeAmountSinceCharge()                * mPowerProfile.getBatteryCapacity()) / 100;        mMaxDrainedPower = (mStats.getHighDischargeAmountSinceCharge()                * mPowerProfile.getBatteryCapacity()) / 100;        processAppUsage(asUsers);//计算每个uid的耗电情况        // Before aggregating apps in to users, collect all apps to sort by their ms per packet.        for (int i=0; i<mUsageList.size(); i++) {            BatterySipper bs = mUsageList.get(i);            bs.computeMobilemspp();            if (bs.mobilemspp != 0) {                mMobilemsppList.add(bs);            }        }        for (int i=0; i<mUserSippers.size(); i++) {            List<BatterySipper> user = mUserSippers.valueAt(i);            for (int j=0; j<user.size(); j++) {                BatterySipper bs = user.get(j);                bs.computeMobilemspp();                if (bs.mobilemspp != 0) {                    mMobilemsppList.add(bs);                }            }        }        Collections.sort(mMobilemsppList, new Comparator<BatterySipper>() {            @Override            public int compare(BatterySipper lhs, BatterySipper rhs) {                if (lhs.mobilemspp < rhs.mobilemspp) {                    return 1;                } else if (lhs.mobilemspp > rhs.mobilemspp) {                    return -1;                }                return 0;            }        });        processMiscUsage();//计算比如屏幕、wifi、蓝牙等耗电        if (DEBUG) {            Log.d(TAG, "Accuracy: total computed=" + makemAh(mComputedPower) + ", min discharge="                    + makemAh(mMinDrainedPower) + ", max discharge=" + makemAh(mMaxDrainedPower));        }        mTotalPower = mComputedPower;        if (mStats.getLowDischargeAmountSinceCharge() > 1) {            if (mMinDrainedPower > mComputedPower) {                double amount = mMinDrainedPower - mComputedPower;                mTotalPower = mMinDrainedPower;                addEntryNoTotal(BatterySipper.DrainType.UNACCOUNTED, 0, amount);//加一个未统计电量            } else if (mMaxDrainedPower < mComputedPower) {//                double amount = mComputedPower - mMaxDrainedPower;                addEntryNoTotal(BatterySipper.DrainType.OVERCOUNTED, 0, amount);//加一个over统计            }        }        Collections.sort(mUsageList);    }

下面先看processAppUsage函数,这个函数是看uid的耗电信息。

private void processAppUsage(SparseArray<UserHandle> asUsers) {        final boolean forAllUsers = (asUsers.get(UserHandle.USER_ALL) != null);............        SparseArray<? extends Uid> uidStats = mStats.getUidStats();        final int NU = uidStats.size();        for (int iu = 0; iu < NU; iu++) {//遍历各个uid            Uid u = uidStats.valueAt(iu);            double p; // in mAs            double power = 0; // in mAs            double highestDrain = 0;            String packageWithHighestDrain = null;            Map<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats();            long cpuTime = 0;            long cpuFgTime = 0;            long wakelockTime = 0;            long gpsTime = 0;                        ..............            if (cpuFgTime > cpuTime) {//先就算每个uid的cpu耗电                if (DEBUG && cpuFgTime > cpuTime + 10000) {                    Log.d(TAG, "WARNING! Cputime is more than 10 seconds behind Foreground time");                }                cpuTime = cpuFgTime; // Statistics may not have been gathered yet.            }            power /= (60*60*1000);            // Process wake lock usage            Map<String, ? extends BatteryStats.Uid.Wakelock> wakelockStats = u.getWakelockStats();            for (Map.Entry<String, ? extends BatteryStats.Uid.Wakelock> wakelockEntry                    : wakelockStats.entrySet()) {                Uid.Wakelock wakelock = wakelockEntry.getValue();                // Only care about partial wake locks since full wake locks                // are canceled when the user turns the screen off.                BatteryStats.Timer timer = wakelock.getWakeTime(BatteryStats.WAKE_TYPE_PARTIAL);                if (timer != null) {                    wakelockTime += timer.getTotalTimeLocked(mRawRealtime, which);                }            }            appWakelockTimeUs += wakelockTime;            wakelockTime /= 1000; // convert to millis            // Add cost of holding a wake lock//计算uid的wakelock耗电            p = (wakelockTime                    * mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_AWAKE)) / (60*60*1000);            if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": wake "                    + wakelockTime + " power=" + makemAh(p));            power += p;            // Add cost of mobile traffic//计算uid移动数据耗电...................            if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": mobile packets "                    + (mobileRx+mobileTx) + " active time " + mobileActive                    + " power=" + makemAh(p));            power += p;            // Add cost of wifi traffic//计算wifi数据耗电            final long wifiRx = u.getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, mStatsType);            final long wifiTx = u.getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, mStatsType);            final long wifiRxB = u.getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, mStatsType);            final long wifiTxB = u.getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, mStatsType);            p = (wifiRx + wifiTx) * wifiPowerPerPacket;            if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": wifi packets "                    + (mobileRx+mobileTx) + " power=" + makemAh(p));            power += p;            // Add cost of keeping WIFI running.            long wifiRunningTimeMs = u.getWifiRunningTime(mRawRealtime, which) / 1000;            mAppWifiRunning += wifiRunningTimeMs;            p = (wifiRunningTimeMs                    * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_ON)) / (60*60*1000);            if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": wifi running "                    + wifiRunningTimeMs + " power=" + makemAh(p));            power += p;            // Add cost of WIFI scans            long wifiScanTimeMs = u.getWifiScanTime(mRawRealtime, which) / 1000;            p = (wifiScanTimeMs                    * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_SCAN)) / (60*60*1000);            if (DEBUG) Log.d(TAG, "UID " + u.getUid() + ": wifi scan " + wifiScanTimeMs                    + " power=" + makemAh(p));            power += p;            for (int bin = 0; bin < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bin++) {                long batchScanTimeMs = u.getWifiBatchedScanTime(bin, mRawRealtime, which) / 1000;                p = ((batchScanTimeMs                        * mPowerProfile.getAveragePower(PowerProfile.POWER_WIFI_BATCHED_SCAN, bin))                    ) / (60*60*1000);                if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": wifi batched scan # " + bin                        + " time=" + batchScanTimeMs + " power=" + makemAh(p));                power += p;            }            // Process Sensor usage//uid传感器耗电...................                p = (multiplier * sensorTime) / (60*60*1000);                if (DEBUG && p != 0) Log.d(TAG, "UID " + u.getUid() + ": sensor #" + sensorHandle                        + " time=" + sensorTime + " power=" + makemAh(p));                power += p;            }            if (DEBUG && power != 0) Log.d(TAG, String.format("UID %d: total power=%s",                    u.getUid(), makemAh(power)));            // Add the app to the list if it is consuming power            final int userId = UserHandle.getUserId(u.getUid());            if (power != 0 || u.getUid() == 0) {//新建一个BatterySipper,BatterySipper 就是耗电的各个参数                BatterySipper app = new BatterySipper(BatterySipper.DrainType.APP, u,                        new double[] {power});                app.cpuTime = cpuTime;                app.gpsTime = gpsTime;                app.wifiRunningTime = wifiRunningTimeMs;                app.cpuFgTime = cpuFgTime;                app.wakeLockTime = wakelockTime;                app.mobileRxPackets = mobileRx;                app.mobileTxPackets = mobileTx;                app.mobileActive = mobileActive / 1000;                app.mobileActiveCount = u.getMobileRadioActiveCount(mStatsType);                app.wifiRxPackets = wifiRx;                app.wifiTxPackets = wifiTx;                app.mobileRxBytes = mobileRxB;                app.mobileTxBytes = mobileTxB;                app.wifiRxBytes = wifiRxB;                app.wifiTxBytes = wifiTxB;                app.packageWithHighestDrain = packageWithHighestDrain;                if (u.getUid() == Process.WIFI_UID) {//如果uid是wifi的,就加到mWifiSippers                    mWifiSippers.add(app);                    mWifiPower += power;                } else if (u.getUid() == Process.BLUETOOTH_UID) {//蓝牙的就加到mBluetoothSippers                    mBluetoothSippers.add(app);                    mBluetoothPower += power;                } else if (!forAllUsers && asUsers.get(userId) == null//如果没有这个userId(多用户),就重新建立一个                        && UserHandle.getAppId(u.getUid()) >= Process.FIRST_APPLICATION_UID) {                    List<BatterySipper> list = mUserSippers.get(userId);                    if (list == null) {                        list = new ArrayList<BatterySipper>();                        mUserSippers.put(userId, list);                    }                    list.add(app);                    if (power != 0) {                        Double userPower = mUserPower.get(userId);                        if (userPower == null) {                            userPower = power;                        } else {                            userPower += power;                        }                        mUserPower.put(userId, userPower);                    }                } else {//或者直接加到mUsageList,mUsageList管理所有的耗电信息list                    mUsageList.add(app);                    if (power > mMaxPower) mMaxPower = power;                    if (power > mMaxRealPower) mMaxRealPower = power;                    mComputedPower += power;                }                if (u.getUid() == 0) {                    osApp = app;                }            }        }

processMiscUsage函数是统计屏幕、wifi耗电等等

    private void processMiscUsage() {        addUserUsage();        addPhoneUsage();        addScreenUsage();        addFlashlightUsage();        addWiFiUsage();        addBluetoothUsage();        addIdleUsage(); // Not including cellular idle power        // Don't compute radio usage if it's a wifi-only device        if (!mWifiOnly) {            addRadioUsage();        }    }


    private void addScreenUsage() {        double power = 0;        long screenOnTimeMs = mStats.getScreenOnTime(mRawRealtime, mStatsType) / 1000;        power += screenOnTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_ON);//时间乘以平均功耗        final double screenFullPower =                mPowerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL);        for (int i = 0; i < BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; i++) {            double screenBinPower = screenFullPower * (i + 0.5f)                    / BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS;            long brightnessTime = mStats.getScreenBrightnessTime(i, mRawRealtime, mStatsType)                    / 1000;            double p = screenBinPower*brightnessTime;            if (DEBUG && p != 0) {                Log.d(TAG, "Screen bin #" + i + ": time=" + brightnessTime                        + " power=" + makemAh(p / (60 * 60 * 1000)));            }            power += p;        }        power /= (60*60*1000); // To hours最后再要除以1小时        if (power != 0) {            addEntry(BatterySipper.DrainType.SCREEN, screenOnTimeMs, power);        }    }

addEntry函数,新建一个BatterySipper,加入mUsageList

    private BatterySipper addEntry(DrainType drainType, long time, double power) {        mComputedPower += power;//计算所有的power        if (power > mMaxRealPower) mMaxRealPower = power;        return addEntryNoTotal(drainType, time, power);    }    private BatterySipper addEntryNoTotal(DrainType drainType, long time, double power) {        if (power > mMaxPower) mMaxPower = power;        BatterySipper bs = new BatterySipper(drainType, null, new double[] {power});//新建一个BatterySipper,加入mUsageList        bs.usageTime = time;        mUsageList.add(bs);        return bs;    }


而如果应用想要获取各个耗电信息,先要refreshStats,再去获取getUsageList,下面是settings中电池那一栏里面的代码

            mStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, profiles);            final List<BatterySipper> usageList = mStatsHelper.getUsageList();

获取后可以根据BatterySipper的value获取每小时的耗电量.


下面我们分析下settings,电池里面的refreshStats函数:

    private void refreshStats() {        mAppListGroup.removeAll();        mAppListGroup.setOrderingAsAdded(false);        mHistPref = new BatteryHistoryPreference(getActivity(), mStatsHelper.getStats(),                mStatsHelper.getBatteryBroadcast());        mHistPref.setOrder(-1);        mAppListGroup.addPreference(mHistPref);        boolean addedSome = false;        final PowerProfile powerProfile = mStatsHelper.getPowerProfile();//每个器件的平均耗电        final BatteryStats stats = mStatsHelper.getStats();        final double averagePower = powerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL);        if (averagePower >= MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP) {//如果没有配置原生的power_profile.xml,这里的averagePower 为0.1            Log.e("TAG", "enter averagePower >= MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP");            final List<UserHandle> profiles = mUm.getUserProfiles();            mStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, profiles);            final List<BatterySipper> usageList = mStatsHelper.getUsageList();//获取各个耗电            final int dischargeAmount = stats != null ? stats.getDischargeAmount(mStatsType) : 0;//BatteryStatsService统计各个的时间会在usb线拔掉后清零,dichargeaAmount相当于usb拔掉后这段时间的电量,相当于电池使用的量            final int numSippers = usageList.size();            for (int i = 0; i < numSippers; i++) {                final BatterySipper sipper = usageList.get(i);                if ((sipper.value * SECONDS_IN_HOUR) < MIN_POWER_THRESHOLD_MILLI_AMP) {//BatterySipper 的power都是以每小时计的                    continue;                }                final double percentOfTotal =                        ((sipper.value / mStatsHelper.getTotalPower()) * dischargeAmount);//和上面一样(sipper.value / mStatsHelper.getTotalPower()统计的值,代表是该统计占该段时间内总的消耗电量的一个比,而该段时间是就是BatteryStats统计的时间,因此这段时间统计的电量还要乘以一个,这段时间内消耗的电量比上总电量的一个值。而且这个消耗电量的统计在BatteryStatsImpl中,当充电时,会把该项消耗电量统计下。                if (((int) (percentOfTotal + .5)) < 1) {                    continue;                }                if (sipper.drainType == BatterySipper.DrainType.OVERCOUNTED) {                    // Don't show over-counted unless it is at least 2/3 the size of                    // the largest real entry, and its percent of total is more significant                    if (sipper.value < ((mStatsHelper.getMaxRealPower()*2)/3)) {                        continue;                    }                    if (percentOfTotal < 10) {                        continue;                    }                    if ("user".equals(Build.TYPE)) {                        continue;                    }                }                if (sipper.drainType == BatterySipper.DrainType.UNACCOUNTED) {                    // Don't show over-counted unless it is at least 1/2 the size of                    // the largest real entry, and its percent of total is more significant                    if (sipper.value < (mStatsHelper.getMaxRealPower()/2)) {                        continue;                    }                    if (percentOfTotal < 5) {                        Log.e("kangkang", "7");                        continue;                    }                    if ("user".equals(Build.TYPE)) {                        continue;                    }                }                final UserHandle userHandle = new UserHandle(UserHandle.getUserId(sipper.getUid()));                final BatteryEntry entry = new BatteryEntry(getActivity(), mHandler, mUm, sipper);                final Drawable badgedIcon = mUm.getBadgedIconForUser(entry.getIcon(),                        userHandle);                final CharSequence contentDescription = mUm.getBadgedLabelForUser(entry.getLabel(),                        userHandle);                final PowerGaugePreference pref = new PowerGaugePreference(getActivity(),                        badgedIcon, contentDescription, entry);                final double percentOfMax = (sipper.value * 100) / mStatsHelper.getMaxPower();                sipper.percent = percentOfTotal;                pref.setTitle(entry.getLabel());                pref.setOrder(i + 1);                pref.setPercent(percentOfMax, percentOfTotal);                if (sipper.uidObj != null) {                    pref.setKey(Integer.toString(sipper.uidObj.getUid()));                }                addedSome = true;                mAppListGroup.addPreference(pref);                if (mAppListGroup.getPreferenceCount() > (MAX_ITEMS_TO_LIST + 1)) {                    break;                }            }        }        if (!addedSome) {//如果走到这里会显示"没有电池数据",要么power_profile.xml没有配置,要么每项都continue            addNotAvailableMessage();        }        BatteryEntry.startRequestQueue();    }


碰到一个问题当电池电量到90%,拔去usb线,settings会显示“没有电池使用数据”,带着这个问题,我们再来看看BatteryStatsImpl:

我们先来看看BatteryStatsHelper中refreshStats函数中的一段代码,如何统计mTotalPower,先会计算一个mComputedPower,会把每个usage的power累计相加,然后下面还有一段代码,mStats.getLowDischargeAmountSinceCharge是什么意思呢?带着这个问题再去看BatteryStatsImpl的代码。

public void refreshStats(int statsType, SparseArray<UserHandle> asUsers, long rawRealtimeUs,        long rawUptimeUs) {。。。。。。。。。。。。        mTotalPower = mComputedPower;        if (mStats.getLowDischargeAmountSinceCharge() > 1) {//            if (mMinDrainedPower > mComputedPower) {                double amount = mMinDrainedPower - mComputedPower;                mTotalPower = mMinDrainedPower;                addEntryNoTotal(BatterySipper.DrainType.UNACCOUNTED, 0, amount);            } else if (mMaxDrainedPower < mComputedPower) {                double amount = mComputedPower - mMaxDrainedPower;                addEntryNoTotal(BatterySipper.DrainType.OVERCOUNTED, 0, amount);            }        }        Collections.sort(mUsageList);    }

先看BatteryStatsImpl中getLowDischargeAmountSinceCharge函数,直接从代码中找哪里改变了这个变量mLowDischargeAmountSinceCharge
    @Override    public int getLowDischargeAmountSinceCharge() {        synchronized(this) {            int val = mLowDischargeAmountSinceCharge;            if (mOnBattery && mDischargeCurrentLevel < mDischargeUnplugLevel) {                val += mDischargeUnplugLevel-mDischargeCurrentLevel-1;            }            return val;        }    }
发现在setOnBatteryLocked函数中会改变其值,而setOnBatteryLocked函数是有BatteryService调用,只有当充电方式改变才会掉,而我们这段代码代表,只有插上usb线会调用,level代表当前电量,mDischargeUnplugLevel代表上一次拔去usb线的电量,这就代表上次拔去usb线后,电量level减少了,就是耗电了,在这次充电的时候把之前的耗电统计下,放在mLowDischargeAmountSinceCharge 和mHighDischargeAmountSinceCharge 这两个变量中,这两个变量差1,就是有一个误差的范围而已。

    void setOnBatteryLocked(final long mSecRealtime, final long mSecUptime, final boolean onBattery,            final int oldStatus, final int level) {        else {            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) {//level代表当前电量,mDischargeUnplugLevel代表上一次拔去usb线的电量                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;        }

现在我们再来看看getLowDischargeAmountSinceCharge函数。

    @Override    public int getLowDischargeAmountSinceCharge() {        synchronized(this) {            int val = mLowDischargeAmountSinceCharge;            if (mOnBattery && mDischargeCurrentLevel < mDischargeUnplugLevel) {//表示现在正在用电状态,mDischargeCurrentLevel 变量代表用电的时候的电量,会时时更新,mDischargeUnplugLevel代表上一次拔去usb线的一个电量,也就是最近的一次消耗电量                val += mDischargeUnplugLevel-mDischargeCurrentLevel-1;            }            return val;        }    }

最近一次的消耗电量加上之前累计的消耗电量就是总的消耗电量了。

下面看看几个关键变量

    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) {        。。。。。。。。。。            addHistoryRecordLocked(mSecRealtime, mSecUptime);            mDischargeCurrentLevel = mDischargeUnplugLevel = level;//刚拔去usb线的时候统计下mDischargeCurrentLevel和mDischargeUnplugLevel值            if (screenOn) {                mDischargeScreenOnUnplugLevel = level;                mDischargeScreenOffUnplugLevel = 0;            } else {                mDischargeScreenOnUnplugLevel = 0;                mDischargeScreenOffUnplugLevel = level;            }            mDischargeAmountScreenOn = 0;            mDischargeAmountScreenOff = 0;            updateTimeBasesLocked(true, !screenOn, uptime, realtime);        } else {            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;//刚插上usb线的时候统计下mDischargeCurrentLevel和mDischargePlugLevel值
setBatteryState是BatteryService时时调的,而setOnBatteryLocked只有充电方式改变后才会调用。

    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;//只要使用电池该mDischargeCurrentLevel 也是时时更新。                if (!mRecordingHistory) {                    mRecordingHistory = true;                    startRecordingHistory(elapsedRealtime, uptime, true);                }            } else if (level < 96) {                if (!mRecordingHistory) {                    mRecordingHistory = true;                    startRecordingHistory(elapsedRealtime, uptime, true);                }            }

再回到前面一开始的BatteryStatsHelper中的mTotalPower问题,

public void refreshStats(int statsType, SparseArray<UserHandle> asUsers, long rawRealtimeUs,        long rawUptimeUs) {。。。。。。。。。。。。        mTotalPower = mComputedPower;        if (mStats.getLowDischargeAmountSinceCharge() > 1) {//只要统计消耗的电量大于1            if (mMinDrainedPower > mComputedPower) {//如果实际总的消耗电量比统计的电量大,那么mTotalPower 就用实际消耗的电量                double amount = mMinDrainedPower - mComputedPower;                mTotalPower = mMinDrainedPower;                addEntryNoTotal(BatterySipper.DrainType.UNACCOUNTED, 0, amount);//并且将没有统计到的电量,也放在usgaelist中            } else if (mMaxDrainedPower < mComputedPower) {                double amount = mComputedPower - mMaxDrainedPower;                addEntryNoTotal(BatterySipper.DrainType.OVERCOUNTED, 0, amount);            }        }        Collections.sort(mUsageList);    }

再看看mMinDrainedPower 和mMaxDrainedPower 两个变量,就是消耗电量除以100,乘以总电量就是消耗的实际电量而不是百分比了。

        mMinDrainedPower = (mStats.getLowDischargeAmountSinceCharge()                * mPowerProfile.getBatteryCapacity()) / 100;        mMaxDrainedPower = (mStats.getHighDischargeAmountSinceCharge()                * mPowerProfile.getBatteryCapacity()) / 100;


回过头来看看settings为什么90%之后,拔掉usb线就没有电池数据了,settings会遍历所有的usagelist,只有所有的都continue才会出现没有电池数据,一个可能及时dischargeAmount为0,mStatsHelper.getTotalPower,和sipper.value

            mStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, profiles);            final List<BatterySipper> usageList = mStatsHelper.getUsageList();            final int dischargeAmount = stats != null ? stats.getDischargeAmount(mStatsType) : 0;                        final int numSippers = usageList.size();            for (int i = 0; i < numSippers; i++) {                final BatterySipper sipper = usageList.get(i);                if ((sipper.value * SECONDS_IN_HOUR) < MIN_POWER_THRESHOLD_MILLI_AMP) {                    continue;                }                final double percentOfTotal =                        ((sipper.value / mStatsHelper.getTotalPower()) * dischargeAmount);                                if (((int) (percentOfTotal + .5)) < 1) {                    continue;                }                if (sipper.drainType == BatterySipper.DrainType.OVERCOUNTED) {                    // Don't show over-counted unless it is at least 2/3 the size of                    // the largest real entry, and its percent of total is more significant                    if (sipper.value < ((mStatsHelper.getMaxRealPower()*2)/3)) {                        continue;                    }                    if (percentOfTotal < 10) {                        continue;                    }                    if ("user".equals(Build.TYPE)) {                        continue;                    }                }                if (sipper.drainType == BatterySipper.DrainType.UNACCOUNTED) {                    // Don't show over-counted unless it is at least 1/2 the size of                    // the largest real entry, and its percent of total is more significant                    if (sipper.value < (mStatsHelper.getMaxRealPower()/2)) {                        continue;                    }                    if (percentOfTotal < 5) {                        continue;                    }                    if ("user".equals(Build.TYPE)) {                        continue;                    }                }

而我们看看BatteryStatsImpl中getDischargeAmount就是getHighDischargeAmountSinceCharge函数,而getHighDischargeAmountSinceCharge就是统计了电池消耗的电量,只是范围比较大的那个值,所有到90%没有关系。

    @Override    public int getDischargeAmount(int which) {        int dischargeAmount = which == STATS_SINCE_CHARGED                ? getHighDischargeAmountSinceCharge()                : (getDischargeStartLevel() - getDischargeCurrentLevel());        if (dischargeAmount < 0) {            dischargeAmount = 0;        }        return dischargeAmount;    }

mStatsHelper.getTotalPower也是BatteryStatsHelper统计的消耗电量,和到90%也没有关系。

那么剩下只有一个可能,sipper.value 为0. 而每个usage的消耗电量为0,只有是统计的时间为0.

再去BatteryStatsImpl看看什么时候会将统计的时间置为0.
在resetAllStatsLocked接口里会把所有的timer重新reset,之后看看谁调用了这个函数

   private void resetAllStatsLocked() {        mStartCount = 0;        initTimes(SystemClock.uptimeMillis() * 1000, SystemClock.elapsedRealtime() * 1000);        mScreenOnTimer.reset(false);        for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {            mScreenBrightnessTimer[i].reset(false);        }        mInteractiveTimer.reset(false);        mLowPowerModeEnabledTimer.reset(false);        mPhoneOnTimer.reset(false);        mAudioOnTimer.reset(false);        mVideoOnTimer.reset(false);        mFlashlightOnTimer.reset(false);        for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {            mPhoneSignalStrengthsTimer[i].reset(false);        }        mPhoneSignalScanningTimer.reset(false);        for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {            mPhoneDataConnectionsTimer[i].reset(false);        }
结果就发现在setOnBatteryLocked
    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) {            // We will reset our status if we are unplugging after the            // battery was last full, or the level is at 100, or            // we have gone through a significant charge (from a very low            // level to a now very high level).            boolean reset = false;            if (!mNoAutoReset && (oldStatus == BatteryManager.BATTERY_STATUS_FULL                    || level >= 90//当当前电量大于90%                    || (mDischargeCurrentLevel < 20 && level >= 80)//当上次插电时的电量是20,现在充电充到80                    || (getHighDischargeAmountSinceCharge() >= 200                            && mHistoryBuffer.dataSize() >= MAX_HISTORY_BUFFER))) {                Slog.i(TAG, "Resetting battery stats: level=" + level + " status=" + oldStatus                        + " dischargeLevel=" + mDischargeCurrentLevel                        + " lowAmount=" + getLowDischargeAmountSinceCharge()                        + " highAmount=" + getHighDischargeAmountSinceCharge());                // Before we write, collect a snapshot of the final aggregated                // stats to be reported in the next checkin.  Only do this if we have                // a sufficient amount of data to make it interesting.                if (getLowDischargeAmountSinceCharge() >= 20) {                    final Parcel parcel = Parcel.obtain();                    writeSummaryToParcel(parcel, true);                    BackgroundThread.getHandler().post(new Runnable() {                        @Override public void run() {                            synchronized (mCheckinFile) {                                FileOutputStream stream = null;                                try {                                    stream = mCheckinFile.startWrite();                                    stream.write(parcel.marshall());                                    stream.flush();                                    FileUtils.sync(stream);                                    stream.close();                                    mCheckinFile.finishWrite(stream);                                } catch (IOException e) {                                    Slog.w("BatteryStats",                                            "Error writing checkin battery statistics", e);                                    mCheckinFile.failWrite(stream);                                } finally {                                    parcel.recycle();                                }                            }                        }                    });                }                doWrite = true;                resetAllStatsLocked();//会将所有的timer,重置                mDischargeStartLevel = level;                reset = true;                mNumDischargeStepDurations = 0;            }
好了,现在有答案了,当电量大于90的时候,BatteryStatsImpl会将所有timer重置,导致BatteryStatsHelper的各个Usage统计的电量为0,之后Settings就会在遍历每个usage的时候continue,然后就会显示没有电池使用数据了。




1 0
原创粉丝点击