您的位置:首页 > 移动开发 > Android开发

Android BatteryStatsHelper深入理解(and5.1)

2015-08-04 14:47 429 查看
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_***ERAGE_POWER_THRESHOLD_MILLI_AMP) {//如果没有配置原生的power_profile.xml,这里的averagePower 为0.1
Log.e("TAG", "enter averagePower >= MIN_***ERAGE_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,然后就会显示没有电池使用数据了。



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: