Android功耗统计算法

基于Android 6.0的源码剖析。

Power_profile.xml文件demo:

<?xml version="1.0" encoding="utf-8"?>
<!--
**
** Copyright 2009, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License")
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
**     http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->

<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">200</item>  <!-- ~200mA -->
  <item name="screen.full">300</item>  <!-- ~300mA -->
  <item name="bluetooth.active">80</item> <!-- Bluetooth data transfer, ~10mA -->
  <item name="bluetooth.on">1</item>  <!-- Bluetooth on & connectable, but not connected, ~0.1mA -->
  <item name="wifi.on">3</item>  <!-- ~3mA -->
  <item name="wifi.active">200</item>  <!-- WIFI data transfer, ~200mA -->
  <item name="wifi.scan">100</item>  <!-- WIFI network scanning, ~100mA -->
  <item name="dsp.audio">10</item> <!-- ~10mA -->
  <item name="dsp.video">50</item> <!-- ~50mA -->
  <item name="camera.flashlight">160</item> <!-- Avg. power for camera flash, ~160mA -->
  <item name="camera.avg">550</item> <!-- Avg. power use of camera in standard usecases, ~550mA -->
  <item name="gps.on">50</item> <!-- ~50mA -->

  <!-- Radio related values. For modems without energy reporting support in firmware, use
       radio.active, radio.scanning, and radio.on. -->
  <item name="radio.active">200</item> <!-- ~200mA -->
  <item name="radio.scanning">10</item> <!-- cellular radio scanning for signal, ~10mA -->
  <!-- Current consumed by the radio at different signal strengths, when paging -->
  <array name="radio.on"> <!-- Strength 0 to BINS-1 -->
      <value>2</value> <!-- ~2mA -->
      <value>1</value> <!-- ~1mA -->
  </array>

  <!-- Radio related values. For modems WITH energy reporting support in firmware, use
       modem.controller.idle, modem.controller.tx, modem.controller.rx, modem.controller.voltage.
       -->
  <item name="modem.controller.idle">0</item>
  <item name="modem.controller.rx">0</item>
  <item name="modem.controller.tx">0</item>
  <item name="modem.controller.voltage">0</item>

  <!-- A list of heterogeneous CPU clusters, where the value for each cluster represents the
       number of CPU cores for that cluster.

       Ex:
       <array name="cpu.clusters.cores">
         <value>4</value> // cluster 0 has cpu0, cpu1, cpu2, cpu3
         <value>2</value> // cluster 1 has cpu4, cpu5, cpu6, cpu7
       </array> -->
  <array name="cpu.clusters.cores">
    <value>4</value>
    <value>4</value>
  </array>

    <!-- Different CPU speeds for cluster 0 as reported in
       /sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state.

       There must be one of these for each cluster, labeled:
       cpu.speeds.cluster0, cpu.speeds.cluster1, etc...
       注释:带cluster就是区分大小核的细分了-->
    <array name="cpu.speeds.cluster0">
        <value>900000</value>
        <value>979000</value>
        <value>1085000</value>
        <value>1218000</value>
        <value>1351000</value>
        <value>1484000</value>
        <value>1617000</value>
        <value>1750000</value>
        <value>1779000</value>
        <value>1809000</value>
        <value>1838000</value>
        <value>1868000</value>
        <value>1897000</value>
        <value>1927000</value>
        <value>1961000</value>
        <value>2001000</value>
    </array>

    <array name="cpu.speeds.cluster1">
        <value>900000</value>
        <value>979000</value>
        <value>1085000</value>
        <value>1218000</value>
        <value>1351000</value>
        <value>1484000</value>
        <value>1617000</value>
        <value>1750000</value>
        <value>1779000</value>
        <value>1809000</value>
        <value>1838000</value>
        <value>1868000</value>
        <value>1897000</value>
        <value>1927000</value>
        <value>1961000</value>
        <value>2001000</value>
    </array>

  <!-- Current at each CPU speed for cluster 0, as per ‘cpu.speeds.cluster0‘.
       Like cpu.speeds.cluster0, there must be one of these present for
       each heterogeneous CPU cluster. -->
    <array name="cpu.active.cluster0">
         <value>254.95</value>
         <value>354.95</value>
         <value>387.15</value>
         <value>442.86</value>
         <value>510.20</value>
         <value>582.65</value>
         <value>631.99</value>
         <value>812.02</value>
         <value>858.84</value>
         <value>943.23</value>
         <value>992.45</value>
         <value>1086.32</value>
         <value>1151.96</value>
         <value>1253.80</value>
         <value>1397.67</value>
         <value>1450.67</value>
    </array>

    <array name="cpu.active.cluster1">
        <value>254.95</value>
        <value>354.95</value>
        <value>387.15</value>
        <value>442.86</value>
        <value>510.20</value>
        <value>582.65</value>
        <value>631.99</value>
        <value>812.02</value>
        <value>858.84</value>
        <value>943.23</value>
        <value>992.45</value>
        <value>1086.32</value>
        <value>1151.96</value>
        <value>1253.80</value>
        <value>1397.67</value>
        <value>1450.67</value>
    </array>

  <!-- Current when CPU is idle -->
  <item name="cpu.idle">10</item>

  <!-- Memory bandwidth power values in mA at the rail. There must be one value
       for each bucket defined in the device tree. -->
  <array name="memory.bandwidths">
    <value>22.7</value> <!-- mA for bucket: 100mb/s-1.5 GB/s memory bandwidth -->
  </array>

  <!-- This is the battery capacity in mAh (measured at nominal voltage) -->
  <item name="battery.capacity">3000</item>

  <!-- Wifi related values. -->
  <!-- Idle Receive current for wifi radio in mA. 0 by default-->
  <item name="wifi.controller.idle">10</item>
  <!-- Rx current for wifi radio in mA. 0 by default-->
  <item name="wifi.controller.rx">150</item>
  <!-- Tx current for wifi radio in mA. 0 by default-->
  <item name="wifi.controller.tx">150</item>
  <!-- Current at each of the wifi Tx levels in mA. The number of tx levels varies per device
       and is available only of wifi chipsets which support the tx level reporting. Use
        wifi.tx for other chipsets. none by default -->
  <array name="wifi.controller.tx_levels"> <!-- mA -->
  </array>
  <!-- Operating volatage for wifi radio in mV. 0 by default-->
  <item name="wifi.controller.voltage">0</item>

  <array name="wifi.batchedscan"> <!-- mA -->
    <value>.2</value> <!-- 1-8/hr -->
    <value>2</value>  <!-- 9-64/hr -->
    <value>20</value>   <!-- 65-512/hr -->
    <value>200</value>    <!-- 513-4,096/hr -->
    <value>500</value>    <!-- 4097-/hr -->
  </array>

</device>

一、 概述

Android系统中的耗电统计分为软件排行榜和硬件排行榜,软件排序榜是统计每个App的耗电总量的排行榜,硬件排行榜则是统计主要硬件的耗电总量的排行榜。

涉及耗电统计相关的核心类:

/framework/base/core/res/res/xml/power_profile.xml
/framework/base/core/java/com/andorid/internal/os/PowerProfile.java
/framework/base/core/java/com/andorid/internal/os/BatteryStatsHelper.java
/framework/base/core/java/com/andorid/internal/os/BatterySipper.java

PowerProfile.java用于获取各个组件的电流数值;power_profile.xml是一个可配置的功耗数据文件。
软件排行榜的计算算法:BatteryStatsHelper类中的processAppUsage()方法。
硬件排行榜的计算算法:BatteryStatsHelper类中的processMiscUsage()方法。

二、软件排行榜

processAppUsage统计每个App的耗电情况

private void processAppUsage(SparseArray<UserHandle> asUsers) {
    //判断是否统计所有用户的App耗电使用情况,目前该参数为true
    final boolean forAllUsers = (asUsers.get(UserHandle.USER_ALL) != null);
    mStatsPeriod = mTypeBatteryRealtime; //耗电的统计时长
    BatterySipper osSipper = null;

    //获取每个uid的统计信息
    final SparseArray<? extends Uid> uidStats = mStats.getUidStats();
    final int NU = uidStats.size();
    // 开始遍历每个uid的耗电情况
    for (int iu = 0; iu < NU; iu++) {
        final Uid u = uidStats.valueAt(iu);
        final BatterySipper app = new BatterySipper(BatterySipper.DrainType.APP, u, 0);
        mCpuPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
        mWakelockPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
        mMobileRadioPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
        mWifiPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
        mBluetoothPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
        mSensorPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
        mCameraPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);
        mFlashlightPowerCalculator.calculateApp(app, u, mRawRealtime, mRawUptime, mStatsType);

        final double totalPower = app.sumPower(); //对App的8项耗电再进行累加。

        //将app添加到 app list, WiFi, Bluetooth等,或其他用户列表
        if (totalPower != 0 || u.getUid() == 0) {

            final int uid = app.getUid();
            final int userId = UserHandle.getUserId(uid);
            if (uid == Process.WIFI_UID) { //uid为wifi的情况
                mWifiSippers.add(app);
            } else if (uid == Process.BLUETOOTH_UID) {//uid为蓝牙的情况
                mBluetoothSippers.add(app);
            } else if (!forAllUsers && asUsers.get(userId) == null
                    && UserHandle.getAppId(uid) >= Process.FIRST_APPLICATION_UID) {
                //就目前来说 forAllUsers=true,不会进入此分支。
                List<BatterySipper> list = mUserSippers.get(userId);
                if (list == null) {
                    list = new ArrayList<>();
                    mUserSippers.put(userId, list);
                }
                list.add(app);
            } else {
                mUsageList.add(app);  //把app耗电加入到mUsageList
            }
            if (uid == 0) {
                osSipper = app; // root用户,代表操作系统的耗电量
            }
        }
    }
    //app之外的耗电量
    if (osSipper != null) {
        mWakelockPowerCalculator.calculateRemaining(osSipper, mStats, mRawRealtime, mRawUptime, mStatsType);
        osSipper.sumPower();
    }
}

流程分析:mTypeBatteryRealtime

mTypeBatteryRealtime = mStats.computeBatteryRealtime(rawRealtimeUs, mStatsType);
private int mStatsType = BatteryStats.STATS_SINCE_CHARGED;

BatteryStats.STATS_SINCE_CHARGED,计算规则是从上次充满电后数据;另外STATS_SINCE_UNPLUGGED是拔掉USB线后的数据。说明充电时间的计算是从上一次拔掉设备到现在的耗电量统计。

耗电计算项

8大模块的耗电计算器,都继承与PowerCalculator抽象类

计算项 Class文件
CPU功耗 mCpuPowerCalculator.java
Wakelock功耗 mWakelockPowerCalculator.java
无线电功耗 mMobileRadioPowerCalculator.java
WIFI功耗 mWifiPowerCalculator.java
蓝牙功耗 mBluetoothPowerCalculator.java
Sensor功耗 mSensorPowerCalculator.java
相机功耗 mCameraPowerCalculator.java
闪光灯功耗 mFlashlightPowerCalculator.java

计算值添加到列表

mWifiSippers.add(app): uid为wifi的情况.
mBluetoothSippers.add(app): uid为蓝牙的情况.
mUsageList.add(app): app耗电加入到mUsageList.
osSipper: root用户,代表操作系统的耗电量,app之外的wakelock耗电也计算该项.

公式:

processAppUsage统计的是Uid。一般地来说每个App都对应一个Uid,但存在以下特殊情况,如果两个或多个App签名和sharedUserId相同,则在运行时,他们拥有相同Uid。对于系统应用uid为system,则统一算入system应用的耗电量。

Uid_Power = process_1_Power + … + process_N_Power,其中所有进程都是属于同一个uid。 当同一的uid下,只有一个进程时,Uid_Power = process_Power;

其中process_Power = CPU功耗 + Wakelock功耗 + 无线电功耗 + WIFI功耗 + 蓝牙功耗 + Sensor功耗 + 相机功耗 + 闪光灯功耗。 接下来开始分配说明每一项的功耗计算公式。

2.1 CPU

CPU功耗项的计算是通过CpuPowerCalculator类,初始化:

public CpuPowerCalculator(PowerProfile profile) {
    final int speedSteps = profile.getNumSpeedSteps(); //获取cpu的主频等级的级数
    mPowerCpuNormal = new double[speedSteps]; //用于记录不同频率下的功耗值
    mSpeedStepTimes = new long[speedSteps];
    for (int p = 0; p < speedSteps; p++) {
        mPowerCpuNormal[p] = profile.getAveragePower(PowerProfile.POWER_CPU_ACTIVE, p);
    }
}

从对象的构造方法,可以看出PowerProfile类提供相应所需的基础功耗值,而真正的功耗值数据来源于power_profile.xml文件,故可以通过配置合理的基础功耗值,来达到较为精准的功耗统计结果。后续所有的PowerCalculator子类在构造方法中都有会相应所需的功耗配置项。

CPU功耗可配置项:

POWER_CPU_SPEEDS = “cpu.speeds” 所对应的数组,配置CPU的频点,频点个数取决于CPU硬件特性
POWER_CPU_ACTIVE = “cpu.active” 所对应的数组,配置CPU频点所对应的单位功耗(mAh)

功耗计算:

public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, long rawUptimeUs, int statsType) {
        final int speedSteps = mSpeedStepTimes.length;
        long totalTimeAtSpeeds = 0;
        for (int step = 0; step < speedSteps; step++) {
            //获取Cpu不同频点下的运行时间
            mSpeedStepTimes[step] = u.getTimeAtCpuSpeed(step, statsType);
            totalTimeAtSpeeds += mSpeedStepTimes[step];
        }
        totalTimeAtSpeeds = Math.max(totalTimeAtSpeeds, 1); //获取Cpu总共的运行时间
        app.cpuTimeMs = (u.getUserCpuTimeUs(statsType) + u.getSystemCpuTimeUs(statsType)) / 1000; //获取cpu在用户态和内核态的执行时长

        double cpuPowerMaMs = 0;
        // 计算Cpu的耗电量
        for (int step = 0; step < speedSteps; step++) {
            final double ratio = (double) mSpeedStepTimes[step] / totalTimeAtSpeeds;
            final double cpuSpeedStepPower = ratio * app.cpuTimeMs * mPowerCpuNormal[step];
            cpuPowerMaMs += cpuSpeedStepPower;
        }

        //追踪不同进程的耗电情况
        double highestDrain = 0;
        app.cpuFgTimeMs = 0;
        final ArrayMap<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats();
        final int processStatsCount = processStats.size();
        //统计同一个uid的不同进程的耗电情况
        for (int i = 0; i < processStatsCount; i++) {
            final BatteryStats.Uid.Proc ps = processStats.valueAt(i);
            final String processName = processStats.keyAt(i);
            app.cpuFgTimeMs += ps.getForegroundTime(statsType);
            final long costValue = ps.getUserTime(statsType) + ps.getSystemTime(statsType) + ps.getForegroundTime(statsType);
            //App可以有多个packages和多个不同的进程,跟踪耗电最大的进程
            if (app.packageWithHighestDrain == null ||
                    app.packageWithHighestDrain.startsWith("*")) {
                highestDrain = costValue;
                app.packageWithHighestDrain = processName;
            } else if (highestDrain < costValue && !processName.startsWith("*")) {
                highestDrain = costValue;
                app.packageWithHighestDrain = processName;
            }
        }
        //当Cpu前台时间 大于Cpu时间,将cpuFgTimeMs赋值为cpuTimeMs
        if (app.cpuFgTimeMs > app.cpuTimeMs) {
            app.cpuTimeMs = app.cpuFgTimeMs;
        }

        app.cpuPowerMah = cpuPowerMaMs / (60 * 60 * 1000); //转换为mAh
    }
}

子公式: cpuPower = ratio_1 * cpu_time * cpu_ratio_1_power + … +ratio_n * cpu_time * cpu_ratio_n_power. 其中: ratio_i = cpu_speed_time/ cpu_speeds_total_time,(i=1,2,…,N,N为CPU频点个数).

2.2 Wakelock

Wakelock功耗项的计算是通过WakelockPowerCalculator类

初始化:

public WakelockPowerCalculator(PowerProfile profile) {
    mPowerWakelock = profile.getAveragePower(PowerProfile.POWER_CPU_AWAKE);
}

Wakelock功耗可配置项:power_profile.xml文件:

POWER_CPU_AWAKE = “cpu.awake” 所对应的值.

功耗计算:

public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, long rawUptimeUs, int statsType) {
    long wakeLockTimeUs = 0;
    final ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> wakelockStats =
            u.getWakelockStats();
    final int wakelockStatsCount = wakelockStats.size();
    for (int i = 0; i < wakelockStatsCount; i++) {
        final BatteryStats.Uid.Wakelock wakelock = wakelockStats.valueAt(i);
        //只统计partial wake locks,由于 full wake locks在灭屏后会自动取消
        BatteryStats.Timer timer = wakelock.getWakeTime(BatteryStats.WAKE_TYPE_PARTIAL);
        if (timer != null) {
            wakeLockTimeUs += timer.getTotalTimeLocked(rawRealtimeUs, statsType);
        }
    }
    app.wakeLockTimeMs = wakeLockTimeUs / 1000; //转换单位为秒
    mTotalAppWakelockTimeMs += app.wakeLockTimeMs;
    //计算唤醒功耗
    app.wakeLockPowerMah = (app.wakeLockTimeMs * mPowerWakelock) / (1000*60*60);
}

子公式:

wakeLockPowerMah = (app.wakeLockTimeMs * mPowerWakelock) / (1000*60*60);

2.3 Wifi

(1)WifiPowerCalculator

Wifi功耗项的计算是通过WifiPowerCalculator类

初始化:

public WifiPowerCalculator(PowerProfile profile) {
        mIdleCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE);
        mTxCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX);
        mRxCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX);
    }

Wifi功耗可配置项:

POWER_WIFI_CONTROLLER_IDLE = “wifi.controller.idle” 项所对应的值.
POWER_WIFI_CONTROLLER_RX = “wifi.controller.rx” 项所对应的值.
POWER_WIFI_CONTROLLER_TX = “wifi.controller.tx” 项所对应的值.

功耗计算:

public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, long rawUptimeUs, int statsType) {
    final long idleTime = u.getWifiControllerActivity(BatteryStats.CONTROLLER_IDLE_TIME, statsType);
    final long txTime = u.getWifiControllerActivity(BatteryStats.CONTROLLER_TX_TIME, statsType);
    final long rxTime = u.getWifiControllerActivity(BatteryStats.CONTROLLER_RX_TIME, statsType);
    app.wifiRunningTimeMs = idleTime + rxTime + txTime; //计算wifi的时间
    app.wifiPowerMah = ((idleTime * mIdleCurrentMa) + (txTime * mTxCurrentMa) + (rxTime * mRxCurrentMa)) / (1000*60*60);
    mTotalAppPowerDrain += app.wifiPowerMah;
    app.wifiRxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_RX_DATA,
            statsType);
    app.wifiTxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_TX_DATA,
            statsType);
    app.wifiRxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_RX_DATA,
            statsType);
    app.wifiTxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_TX_DATA,
            statsType);
}

子公式:

wifiPowerMah = ((idleTime * mIdleCurrentMa) + (txTime * mTxCurrentMa) + (rxTime * mRxCurrentMa)) / (1000* 60* 60);

(2)WifiPowerEstimator

可配置项:

POWER_WIFI_ACTIVE = “wifi.active”(用于计算mWifiPowerPerPacket)
POWER_WIFI_ON=”wifi.on”
POWER_WIFI_SCAN = “wifi.scan”
POWER_WIFI_BATCHED_SCAN = “wifi.batchedscan”;
功耗计算:

public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, long rawUptimeUs, int statsType) {
    app.wifiRxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_RX_DATA,
            statsType);
    app.wifiTxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_TX_DATA,
            statsType);
    app.wifiRxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_RX_DATA,
            statsType);
    app.wifiTxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_TX_DATA,
            statsType);
    final double wifiPacketPower = (app.wifiRxPackets + app.wifiTxPackets)
            * mWifiPowerPerPacket;
    app.wifiRunningTimeMs = u.getWifiRunningTime(rawRealtimeUs, statsType) / 1000;
    mTotalAppWifiRunningTimeMs += app.wifiRunningTimeMs;
    final double wifiLockPower = (app.wifiRunningTimeMs * mWifiPowerOn) / (1000*60*60);
    final long wifiScanTimeMs = u.getWifiScanTime(rawRealtimeUs, statsType) / 1000;
    final double wifiScanPower = (wifiScanTimeMs * mWifiPowerScan) / (1000*60*60);
    double wifiBatchScanPower = 0;
    for (int bin = 0; bin < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bin++) {
        final long batchScanTimeMs =
                u.getWifiBatchedScanTime(bin, rawRealtimeUs, statsType) / 1000;
        final double batchScanPower = (batchScanTimeMs * mWifiPowerBatchScan) / (1000*60*60);
        wifiBatchScanPower += batchScanPower;
    }
    app.wifiPowerMah = wifiPacketPower + wifiLockPower + wifiScanPower + wifiBatchScanPower;
}

其中 BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS=5

另外

private static double getWifiPowerPerPacket(PowerProfile profile) {
    final long WIFI_BPS = 1000000; //初略估算每秒的收发1000000bit
    final double WIFI_POWER = profile.getAveragePower(PowerProfile.POWER_WIFI_ACTIVE)
            / 3600;
    return (WIFI_POWER / (((double)WIFI_BPS) / 8 / 2048)) / (60*60);
}

子公式:
wifiPowerMah = wifiPacketPower + wifiLockPower + wifiScanPower + wifiBatchScanPower;
wifiPacketPower = (wifiRxPackets + wifiTxPackets) * mWifiPowerPerPacket;
wifiLockPower = (wifiRunningTimeMs * mWifiPowerOn) / (1000* 60* 60);
wifiScanPower = (wifiScanTimeMs * mWifiPowerScan) / (1000* 60* 60);
wifiBatchScanPower = ∑ (batchScanTimeMs * mWifiPowerBatchScan) / (1000* 60* 60) ,5次相加。

2.4 Bluetooth

Bluetooth功耗项的计算是通过BluetoothPowerCalculator类

初始化:

public BluetoothPowerCalculator(PowerProfile profile) {
    mIdleMa = profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_IDLE);
    mRxMa = profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_RX);
    mTxMa = profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_TX);
}

Bluetooth功耗可配置项(目前蓝牙功耗计算的方法为空,此配置暂可忽略):

POWER_BLUETOOTH_CONTROLLER_IDLE = “bluetooth.controller.idle” 所对应的值
POWER_BLUETOOTH_CONTROLLER_RX = “bluetooth.controller.rx” 所对应的值
POWER_BLUETOOTH_CONTROLLER_TX = “bluetooth.controller.tx” 所对应的值

功耗计算:

public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, long rawUptimeUs, int statsType) {
    // No per-app distribution yet.
}

子公式:

bluePower = 0;还没有给每个App统计蓝牙的算法。

2.5 Camera

Camera功耗项的计算是通过CameraPowerCalculator类

初始化:

public CameraPowerCalculator(PowerProfile profile) {
    mCameraPowerOnAvg = profile.getAveragePower(PowerProfile.POWER_CAMERA);
}

Camera功耗可配置项:

POWER_CAMERA = “camera.avg” 所对应的值

功耗计算:

public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, long rawUptimeUs, int statsType) {
    //对于camera功耗的评估比较粗,camera打开认定功耗一致
    final BatteryStats.Timer timer = u.getCameraTurnedOnTimer();
    if (timer != null) {
        final long totalTime = timer.getTotalTimeLocked(rawRealtimeUs, statsType) / 1000;
        app.cameraTimeMs = totalTime;
        app.cameraPowerMah = (totalTime * mCameraPowerOnAvg) / (1000*60*60);
    } else {
        app.cameraTimeMs = 0;
        app.cameraPowerMah = 0;
    }
}

子公式:

cameraPowerMah = (totalTime * mCameraPowerOnAvg) / (1000*60*60);

2.6 Flashlight

Flashlight功耗项的计算是通过FlashlightPowerCalculator类

初始化:

public FlashlightPowerCalculator(PowerProfile profile) {
    mFlashlightPowerOnAvg = profile.getAveragePower(PowerProfile.POWER_FLASHLIGHT);
}

Flashlight功耗可配置项:

POWER_FLASHLIGHT = “camera.flashlight” 所对应的值。

功耗计算:

public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, long rawUptimeUs, int statsType) {
    final BatteryStats.Timer timer = u.getFlashlightTurnedOnTimer();
    if (timer != null) {
        final long totalTime = timer.getTotalTimeLocked(rawRealtimeUs, statsType) / 1000;
        app.flashlightTimeMs = totalTime;
        app.flashlightPowerMah = (totalTime * mFlashlightPowerOnAvg) / (1000*60*60);
    } else {
        app.flashlightTimeMs = 0;
        app.flashlightPowerMah = 0;
    }
}

子公式:

flashlightPowerMah = (totalTime * mFlashlightPowerOnAvg) / (1000*60*60);

flashlight计算方式与Camera功耗计算思路一样。

2.7 MobileRadio

无线电功耗项的计算是通过MobileRadioPowerCalculator类

初始化:

public MobileRadioPowerCalculator(PowerProfile profile, BatteryStats stats) {
    mPowerRadioOn = profile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE);
    for (int i = 0; i < mPowerBins.length; i++) {
        mPowerBins[i] = profile.getAveragePower(PowerProfile.POWER_RADIO_ON, i);
    }
    mPowerScan = profile.getAveragePower(PowerProfile.POWER_RADIO_SCANNING);
    mStats = stats;
}

无线电功耗可配置项:

POWER_RADIO_ACTIVE = “radio.active” 所对应的值。

功耗计算:

public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, long rawUptimeUs, int statsType) {
    app.mobileRxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_RX_DATA, statsType);
    app.mobileTxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_TX_DATA, statsType);
    app.mobileActive = u.getMobileRadioActiveTime(statsType) / 1000;
    app.mobileActiveCount = u.getMobileRadioActiveCount(statsType);
    app.mobileRxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_MOBILE_RX_DATA,
            statsType);
    app.mobileTxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_MOBILE_TX_DATA,
            statsType);
    if (app.mobileActive > 0) {
        // 当追踪信号的开头,则采用mobileActive时间乘以相应功耗
        mTotalAppMobileActiveMs += app.mobileActive;
        app.mobileRadioPowerMah = (app.mobileActive * mPowerRadioOn) / (1000*60*60);
    } else {
        //当没有追踪信号开关,则采用收发数据包来评估功耗
        app.mobileRadioPowerMah = (app.mobileRxPackets + app.mobileTxPackets) * getMobilePowerPerPacket(rawRealtimeUs, statsType);
    }
}

计算出每收/发一个数据包的功耗值

private double getMobilePowerPerPacket(long rawRealtimeUs, int statsType) {
    final long MOBILE_BPS = 200000; //Extract average bit rates from system
    final double MOBILE_POWER = mPowerRadioOn / 3600;
    final long mobileRx = mStats.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_RX_DATA,
            statsType);
    final long mobileTx = mStats.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_TX_DATA,
            statsType);
    final long mobileData = mobileRx + mobileTx;
    final long radioDataUptimeMs =
            mStats.getMobileRadioActiveTime(rawRealtimeUs, statsType) / 1000;
    final double mobilePps = (mobileData != 0 && radioDataUptimeMs != 0)
            ? (mobileData / (double)radioDataUptimeMs)
            : (((double)MOBILE_BPS) / 8 / 2048);
    return (MOBILE_POWER / mobilePps) / (60*60);
}

子公式:

情况一:当追踪信号活动时间,即mobileActive > 0,采用:

mobileRadioPowerMah = (app.mobileActive * mPowerRadioOn) / (1000* 60* 60);

情况二:当没有追踪信号活动时间,则采用:

mobileRadioPowerMah = (app.mobileRxPackets + app.mobileTxPackets) * MobilePowerPerPacket

其中MobilePowerPerPacket = ((mPowerRadioOn / 3600) / mobilePps) / (60*60), mobilePps= (mobileRx + mobileTx)/radioDataUptimeMs

2.8 Sensor

Sensor功耗项的计算是通过SensorPowerCalculator类

初始化:

public SensorPowerCalculator(PowerProfile profile, SensorManager sensorManager) {
    mSensors = sensorManager.getSensorList(Sensor.TYPE_ALL);
    mGpsPowerOn = profile.getAveragePower(PowerProfile.POWER_GPS_ON);
}

Sensor功耗可配置项:

POWER_GPS_ON = “gps.on” 所对应的值。

功耗计算:

d calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, long rawUptimeUs, int statsType) {
    // 计算没有个Uid
    final SparseArray<? extends BatteryStats.Uid.Sensor> sensorStats = u.getSensorStats();
    final int NSE = sensorStats.size();
    for (int ise = 0; ise < NSE; ise++) {
        final BatteryStats.Uid.Sensor sensor = sensorStats.valueAt(ise);
        final int sensorHandle = sensorStats.keyAt(ise);
        final BatteryStats.Timer timer = sensor.getSensorTime();
        final long sensorTime = timer.getTotalTimeLocked(rawRealtimeUs, statsType) / 1000;
        switch (sensorHandle) {
            case BatteryStats.Uid.Sensor.GPS:  //对于uid为GPS的情况
                app.gpsTimeMs = sensorTime;
                app.gpsPowerMah = (app.gpsTimeMs * mGpsPowerOn) / (1000*60*60);
                break;
            default:
                final int sensorsCount = mSensors.size();
                for (int i = 0; i < sensorsCount; i++) { //统计所有的sensor情况
                    final Sensor s = mSensors.get(i);
                    if (s.getHandle() == sensorHandle) {
                        app.sensorPowerMah += (sensorTime * s.getPower()) / (1000*60*60);
                        break;
                    }
                }
                break;
        }
    }
}

GPS的功耗计算与sensor的计算方法放在一块

子公式:

gpsPowerMah = (app.gpsTimeMs * mGpsPowerOn) / (1000* 60* 60);

软件功耗总公式

BatterySipper中,功耗计算方法:

public double sumPower() {
    return totalPowerMah = usagePowerMah + wifiPowerMah + gpsPowerMah + cpuPowerMah +
            sensorPowerMah + mobileRadioPowerMah + wakeLockPowerMah + cameraPowerMah +
            flashlightPowerMah;
}

软件功耗的子项共分为9项:

功耗项 解释
usage 通用的功耗
cpu cpu的消耗
wakelock 唤醒带来的功耗
mobileRadio 移动无线的功耗
wifi wifi功耗
gps 定位的功耗
sensor 传感器的功耗
camera 相机功耗
flashlight 闪光灯功耗

目前没有统计蓝牙的耗电,源码中统计蓝牙耗电的方法是空方法。gps耗电的计算在sensor模块一起计算的。

软件功耗总公式:

三、 硬件排行榜

processMiscUsage()

private void processMiscUsage() {
    addUserUsage();
    addPhoneUsage();
    addScreenUsage();
    addWiFiUsage();
    addBluetoothUsage();
    addIdleUsage();
    if (!mWifiOnly) { //对于只有wifi上网功能的设备,将不算计此项
        addRadioUsage();
    }
}

硬件功耗的子项共分为7项:

功耗项 解释
UserUsage 用户功耗
PhoneUsage 通话功耗
ScreenUsage 屏幕功耗
WiFiUsage Wifi功耗
BluetoothUsage 蓝牙消耗
IdleUsage CPU Idle功耗
RadioUsage 移动无线功耗

3.1 User

用户功耗的类型DrainType.USER

功耗计算:

private void addUserUsage() {
    for (int i = 0; i < mUserSippers.size(); i++) {
        final int userId = mUserSippers.keyAt(i);
        BatterySipper bs = new BatterySipper(DrainType.USER, null, 0);
        bs.userId = userId;
        aggregateSippers(bs, mUserSippers.valueAt(i), "User"); //将UserSippers中的功耗都合入bs.
        mUsageList.add(bs);
    }
}

合计:

private void aggregateSippers(BatterySipper bs, List<BatterySipper> from, String tag)  {
    for (int i=0; i<from.size(); i++) {
        BatterySipper wbs = from.get(i);
        bs.add(wbs);  //将from中的功耗数据合入bs
    }
    bs.computeMobilemspp();
    bs.sumPower(); //计算总功耗
}

子公式:

user_power = user_1_power + user_2_power + … + user_n_power; (n为所有的user的总数)

3.2 Phone

通话功耗的类型为DrainType.PHONE

通话功耗可配置项:

POWER_RADIO_ACTIVE = “radio.active” 所对应的值。

功耗计算:

private void addPhoneUsage() {
    long phoneOnTimeMs = mStats.getPhoneOnTime(mRawRealtime, mStatsType) / 1000;
    double phoneOnPower = mPowerProfile.getAveragePower(PowerProfile.POWER_RADIO_ACTIVE) * phoneOnTimeMs / (60*60*1000);
    if (phoneOnPower != 0) {
        addEntry(BatterySipper.DrainType.PHONE, phoneOnTimeMs, phoneOnPower);
    }
}

子公式:

phone_powers = (phoneOnTimeMs * phoneOnPower) / (60* 60* 1000)

3.3 CPU Idle

CPU Idle功耗的类型为DrainType.IDLE

CPU Idle功耗可配置项:

  • PowerProfile.POWER_CPU_IDLE = “cpu.idle” 所对应的值

功耗计算:

private void addIdleUsage() {
    long idleTimeMs = (mTypeBatteryRealtime - mStats.getScreenOnTime(mRawRealtime, mStatsType)) / 1000;
    double idlePower = (idleTimeMs * mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE)) / (60*60*1000);
    if (idlePower != 0) {
        addEntry(BatterySipper.DrainType.IDLE, idleTimeMs, idlePower);
    }
}

子公式:

idlePower = (idleTimeMs * cpuIdlePower) / (60* 60* 1000)

3.4 Screen

屏幕功耗的类型为DrainType.SCREEN

屏幕功耗可配置项

  • POWER_SCREEN_ON = “screen.on” 所对应的值
  • POWER_SCREEN_FULL = “screen.full” 所对应的值

功耗计算:

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;
        power += p;
    }
    power /= (60*60*1000); // To hours
    if (power != 0) {
        addEntry(BatterySipper.DrainType.SCREEN, screenOnTimeMs, power);
    }
}

其中参数:

BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS = 5;

static final String[] SCREEN_BRIGHTNESS_NAMES = {
        "dark", "dim", "medium", "light", "bright"
    };

static final String[] SCREEN_BRIGHTNESS_SHORT_NAMES = {
    "0", "1", "2", "3", "4"
};

子公式:

公式:screen_power = screenOnTimeMs * screenOnPower + backlight_power

其中:backlight_power = 0.1 * dark_brightness_time * screenFullPower + 0.3 * dim_brightness_time * screenFullPower + 0.5 * medium_brightness_time * screenFullPower + 0.7 * light_brightness_time + 0.9 * bright_brightness_time * screenFullPower;

3.5 Wifi

(1)WifiPowerCalculater

Wifi的硬件功耗的类型DrainType.WIFI。

Wifi功耗可配置项

power_profile.xml文件:

  • POWER_WIFI_CONTROLLER_IDLE = “wifi.controller.idle” 所对应的值
  • POWER_WIFI_CONTROLLER_RX = “wifi.controller.rx” 所对应的值
  • POWER_WIFI_CONTROLLER_TX = “wifi.controller.tx” 所对应的值

功耗计算:

wifi的硬件功耗是指除去App消耗之外的剩余wifi功耗

private void addWiFiUsage() {
    BatterySipper bs = new BatterySipper(DrainType.WIFI, null, 0);
    mWifiPowerCalculator.calculateRemaining(bs, mStats, mRawRealtime, mRawUptime, mStatsType); //计算wifi硬件功耗
    aggregateSippers(bs, mWifiSippers, "WIFI"); //将Wifi硬件合计入总功耗
    if (bs.totalPowerMah > 0) {
        mUsageList.add(bs);
    }
}

计算:

@Override
public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs, long rawUptimeUs, int statsType) {
    final long idleTimeMs = stats.getWifiControllerActivity(BatteryStats.CONTROLLER_IDLE_TIME, statsType);
    final long rxTimeMs = stats.getWifiControllerActivity(BatteryStats.CONTROLLER_RX_TIME, statsType);
    final long txTimeMs = stats.getWifiControllerActivity(BatteryStats.CONTROLLER_TX_TIME, statsType);
    app.wifiRunningTimeMs = idleTimeMs + rxTimeMs + txTimeMs;
    double powerDrainMah = stats.getWifiControllerActivity(BatteryStats.CONTROLLER_POWER_DRAIN, statsType) / (double)(1000*60*60);
    if (powerDrainMah == 0) {
        //对于不直接报告功耗的组件,可通过直接计算的方式来获取
        powerDrainMah = ((idleTimeMs * mIdleCurrentMa) + (txTimeMs * mTxCurrentMa) + (rxTimeMs * mRxCurrentMa)) / (1000*60*60);
    }
    app.wifiPowerMah = Math.max(0, powerDrainMah - mTotalAppPowerDrain);
}

子公式:wifiPowerMah = powerDrainMah - mTotalAppPowerDrain;

powerDrainMah = ((idleTimeMs * mIdleCurrentMa) + (txTimeMs * mTxCurrentMa) +(rxTimeMs * mRxCurrentMa);

mTotalAppPowerDrain是通过calculateApp计算过程中获取,记录所有App的wifi功耗值之和。

(2)WifiPowerEstimator

可配置项

POWER_WIFI_ON=”wifi.on”

功耗计算:

public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs, long rawUptimeUs, int statsType) {
    final long totalRunningTimeMs = stats.getGlobalWifiRunningTime(rawRealtimeUs, statsType) / 1000;
    final double powerDrain = ((totalRunningTimeMs - mTotalAppWifiRunningTimeMs) * mWifiPowerOn) / (1000*60*60);
    app.wifiRunningTimeMs = totalRunningTimeMs;
    app.wifiPowerMah = Math.max(0, powerDrain);
}

子公式:

wifiPowerMah = ((totalRunningTimeMs - mTotalAppWifiRunningTimeMs) * mWifiPowerOn) / (1000* 60* 60);

3.6 Bluetooth

蓝牙功耗的类型DrainType.BLUETOOTH

蓝牙功耗可配置项

  • POWER_BLUETOOTH_CONTROLLER_IDLE = “bluetooth.controller.idle” 所对应的值
  • POWER_BLUETOOTH_CONTROLLER_RX = “bluetooth.controller.rx” 所对应的值
  • POWER_BLUETOOTH_CONTROLLER_TX = “bluetooth.controller.tx” 所对应的值

功耗计算:

private void addBluetoothUsage() {
    BatterySipper bs = new BatterySipper(BatterySipper.DrainType.BLUETOOTH, null, 0);
    mBluetoothPowerCalculator.calculateRemaining(bs, mStats, mRawRealtime, mRawUptime, mStatsType);
    aggregateSippers(bs, mBluetoothSippers, "Bluetooth"); //将蓝牙功耗合计入总功耗
    if (bs.totalPowerMah > 0) {
        mUsageList.add(bs);
    }
}

计算:

public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs, long rawUptimeUs, int statsType) {
    final long idleTimeMs = stats.getBluetoothControllerActivity(
            BatteryStats.CONTROLLER_IDLE_TIME, statsType);
    final long txTimeMs = stats.getBluetoothControllerActivity(
            BatteryStats.CONTROLLER_TX_TIME, statsType);
    final long rxTimeMs = stats.getBluetoothControllerActivity(
            BatteryStats.CONTROLLER_RX_TIME, statsType);
    final long totalTimeMs = idleTimeMs + txTimeMs + rxTimeMs;
    double powerMah = stats.getBluetoothControllerActivity(
            BatteryStats.CONTROLLER_POWER_DRAIN, statsType) / (double)(1000*60*60);
    if (powerMah == 0) {
        //对于不直接报告功耗的组件,可通过直接计算的方式来获取
        powerMah = ((idleTimeMs * mIdleMa) + (rxTimeMs * mRxMa) + (txTimeMs * mTxMa)) / (1000*60*60);
    }

    app.usagePowerMah = powerMah;
    app.usageTimeMs = totalTimeMs;
}

子公式:

公式:usagePowerMah = ((idleTimeMs * mIdleMa) + (rxTimeMs * mRxMa) + (txTimeMs * mTxMa)) / (1000 * 60 * 60);

蓝牙的计算与3.5 wifi计算方式相仿。

3.7 MobileRadio

无线电功耗的类型为DrainType.CELL

无线电功耗可配置项

power_profile.xml文件:

  • POWER_RADIO_ON = “radio.on” 所对应的数组,该数组大小为NUM_SIGNAL_STRENGTH_BINS = 5
  • POWER_RADIO_SCANNING = “radio.scanning” 所对应的值

功耗计算:

对于只支持wifi的设备则不需要计算该项。

private void addRadioUsage() {
    BatterySipper radio = new BatterySipper(BatterySipper.DrainType.CELL, null, 0);
    mMobileRadioPowerCalculator.calculateRemaining(radio, mStats, mRawRealtime, mRawUptime, mStatsType);
    radio.sumPower();
    if (radio.totalPowerMah > 0) {
        mUsageList.add(radio);
    }
}

计算:

@Override
public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs, long rawUptimeUs, int statsType) {
    double power = 0;
    long signalTimeMs = 0;
    long noCoverageTimeMs = 0;
    for (int i = 0; i < mPowerBins.length; i++) {
        long strengthTimeMs = stats.getPhoneSignalStrengthTime(i, rawRealtimeUs, statsType)
                / 1000;
        final double p = (strengthTimeMs * mPowerBins[i]) / (60*60*1000);
        power += p;
        signalTimeMs += strengthTimeMs;
        if (i == 0) {
            noCoverageTimeMs = strengthTimeMs;
        }
    }
    final long scanningTimeMs = stats.getPhoneSignalScanningTime(rawRealtimeUs, statsType)
            / 1000;
    final double p = (scanningTimeMs * mPowerScan) / (60*60*1000);
    power += p;
    long radioActiveTimeMs = mStats.getMobileRadioActiveTime(rawRealtimeUs, statsType) / 1000;
    long remainingActiveTimeMs = radioActiveTimeMs - mTotalAppMobileActiveMs;
    if (remainingActiveTimeMs > 0) {
        power += (mPowerRadioOn * remainingActiveTimeMs) / (1000*60*60);
    }
    if (power != 0) {
        if (signalTimeMs != 0) {
            app.noCoveragePercent = noCoverageTimeMs * 100.0 / signalTimeMs;
        }
        app.mobileActive = remainingActiveTimeMs;
        app.mobileActiveCount = stats.getMobileRadioActiveUnknownCount(statsType);
        app.mobileRadioPowerMah = power;
    }
}

常量:

public static final int NUM_SIGNAL_STRENGTH_BINS = 5;
public static final String[] SIGNAL_STRENGTH_NAMES = {
    "none", "poor", "moderate", "good", "great"
};

子公式:
mobileRadioPowerMah = strengthOnPower + scanningPower + remainingActivePower
其中:strengthOnPower = none_strength_Ms * none_strength_Power + poor_strength_Ms * poor_strength_Power + moderate_strength_Ms * moderate_strength_Power + good_strength_Ms * good_strength_Power + great_strength_Ms * great_strength_Power;
scanningPower = scanningTimeMs * mPowerScan;
remainingActivePower = (radioActiveTimeMs - mTotalAppMobileActiveMs)* mPowerRadioOn;

硬件耗电总公式:

四、总结

(1) 关于WifiPowerCalculator && WifiPowerEstimator的选择问题

在 BatteryStatsHelper.java中:

final boolean hasWifiPowerReporting = checkHasWifiPowerReporting(mStats, mPowerProfile);
    if (mWifiPowerCalculator == null || hasWifiPowerReporting != mHasWifiPowerReporting) {
        mWifiPowerCalculator = hasWifiPowerReporting ?
                new WifiPowerCalculator(mPowerProfile) :
                new WifiPowerEstimator(mPowerProfile);
        mHasWifiPowerReporting = hasWifiPowerReporting;
    }

当hasWifiPowerReporting = true时,采用WifiPowerCalculator算法;
当hasWifiPowerReporting = false时,采用WifiPowerEstimator算法;

其中:

public static boolean checkHasWifiPowerReporting(BatteryStats stats, PowerProfile profile) {
    return stats.hasWifiActivityReporting() &&
            profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE) != 0 &&
            profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX) != 0 &&
            profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX) != 0;
}

mHasWifiActivityReporting的默认值为false,故WIFI计算方式默认采用WifiPowerEstimator方式。

(2) 硬件与软件排行榜,功耗项对比:

功耗项 软件榜 硬件榜
CPU
无线电
WIFI
蓝牙
Wakelock -
Sensor -
相机 -
闪光灯 -
通话 -
屏幕 -
用户 -

其中√代表包含此功耗项。

(3) 硬件与软件排行榜,可配置项对比:

对于wifi的统计存在两种方式.

功耗项 软件榜 硬件榜
CPU cpu.active cpu.idle
无线电 radio.active radio.on/scanning
WIFI_Calculater wifi.controller.idle/rx/tx wifi.controller.idle/rx/tx
WIFI_Estimator wifi.on/ scan/ batchedscan wifi.on
蓝牙 bluetooth.controller.idle/rx/tx bluetooth.controller.idle/rx/tx
Wakelock cpu.awake -
Sensor gps.on -
相机 camera.avg -
闪光灯 camera.flashlight -
通话 - radio.active
屏幕 - screen.on
用户 -  

只有通过配置文件,精确地配置好每一项的基础功耗值,才能有一个精确的功耗统计结果。

原文地址:https://www.cnblogs.com/hellokitty2/p/12253075.html

时间: 2024-11-05 19:41:57

Android功耗统计算法的相关文章

Android流量统计TrafficStats类

对于Android流量统计来说在2.2版中新加入了TrafficStats类可以轻松获取,其实本身TrafficStats类也是读取Linux提供的文件对象系统类型的文本进行解析. android.net.TrafficStats类中,提供了多种静态方法,可以直接调用获取,返回类型均为 long型,如果返回等于-1代表 UNSUPPORTED 当前设备不支持统计. static long getMobileRxBytes() //获取通过Mobile连接收到的字节总数,不包含WiFi stati

SEO 统计算法

1)简单粗暴型的,这里不用去管浏览器的user-agent,不管cookie等信息,每产生一次PV,就直接计数,优点:简单,缺点:可能不真实,也可能有刷量数据 2) 稍微细腻点的统计,会区分新老用户,楼主你可以研究下baidu统计的SDK,里面包含有用户的浏览器信息,操作系统信息.用户的地域信息等,也就是说, 你通过浏览器的javascript以及和服务器数据的交互,对于后台服务器来说,是可以获取这些数据的,那么对于站长之家这样的网站,他可能想要统计到 真实的用户访问情况,以便有些行为分析,这时

说说Android LRU缓存算法实现笔记(二)--LRU的应用

上一篇文章说说Android LRU缓存算法实现学习笔记(一) 中我们介绍了最常用的实现LRU缓存的数据结构LinkedHashMap,这一节我们会针对LinkedHashMap的数据结构的特性,来自己实现缓存结构和学习Android源码和项目中对缓存的完善. 上一篇说到对于缓存实现,我们很重要的会考虑以下几点:1.访问速度:2.逐出旧的缓存策略:3.最好还能考虑到一定的并发度.LinkedHashMap对哈希表的实现保证了我们缓存的快速访问速度,我们通过源码知道,LinkedHashMap默认

Android Stduio统计项目的代码行数

android studio统计项目的代码行数的步骤如下: 1)按住Ctrl+Shift+A,在弹出的框输入‘find’,然后选择Find in Path.(或者使用快捷键Ctrl+Shift+F) 2)在弹出Find in Path的框中的Text to find输入\n,接着勾选Regular expression(正则表达式),Context选择anywhere, Scope根据你想要统计的范围进行选择,File mask选择*.java.(在这里统计项目的Java的代码行数) 3)下图的

统计算法学习梳理(一)

研一零零散散用了一些统计算法,在此简单梳理下,力求用电梯演讲法则去阐述每种算法模型(这是初哀,最后,呵呵了).但自己理解不深,还需要进一步努力.更重要的是复用了他人的智慧. 统计学习概述 关于统计学习,首先推荐李航老师著作的一本书<统计学习方法>.在此引用里边一句话来定义统计学习:统计学习(statistical learning)是关于计算机基于数据构建概率模型并运用模型对数据进行预测与分析的一门学科.从中可见,统计学习中有两个重要的点:数据,概率模型. 统计学习方法里边有三个要素:模型,策

Android流量统计TrafficStats类的使用

转自http://gundumw100.iteye.com/blog/1294167 对于Android流量统计来说在2.2版中新加入了TrafficStats类可以轻松获取,其实本身TrafficStats类也是读取Linux提 供的文件对象系统类型的文本进行解析.android.net.TrafficStats类中,提供了多种静态方法,可以直接调用获取,返回类型均为 long型,如果返回等于-1代表 UNSUPPORTED 当前设备不支持统计. static long getMobileRx

统计算法_1

最近不知道写什么了,基本python的各种功能百度一下,都能搜到一大把,最近itchat好像很火,不过对这个不是很感冒,等以后有兴趣或者用的上的时候研究研究准备把统计方面的东西再看看,就写一些简单的统计算法吧,其实这些在python里面都有现成的,但是有句名言"不要只会用,还要知道原理"(是哪个名人说的?如果没有,那就算我说的了),所以写这些供学习之用.那么从头再来,循序渐进,先写这些,本来想写个对数函数的,结果发现真TM难,如果谁能写出来麻烦告知下,我也膜拜大神一下1.算数平均数2.

android google 统计导致的文件冲突

android studio 加入google 统计 1. buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:2.2.3' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' classpath 'com.google.gms:google-services:3.0.0' // NOTE

记录我第一次在Android开发图像处理算法的经历

大概是四月底的时候,有人加我QQ问我是否做能做一些基于图像皮肤检测的算法, 主要是实现对皮肤六项指标: 1.      水分 2.      有份 3.      痤疮与痘痘 4.      色斑与肤色 5.      皱纹 6.      毛孔 听到这些内容,我稍微思考了一下,心里有了点思路,跟他交流了一下,关于怎么 做的思路就更加清晰起来,于是我先基于JAVA语言实现了第一版,然后把它改到 android上面,发现android代码改动很小,就可以在android上运行我的测试程序 了,但是