Android N 电量使用情况之蓝牙耗电量

    遇到的问题:设备电量使用情况中,蓝牙的耗电量很高,都可以达到百分之90。

    第一次尝试解决:

    首先先查看了power_profile.xml,发现该文件没有配置bluetooth.active和bluetooth.on两项,以为自己找到了原因,就把这两项配置了一下,结果,还是不行,又把这两项全部改成0,还是不行,没办法了,只能分析代码了。

(1) BatteryStatsHelper

    系统设置主要通过该类来获取电量使用情况,通过调用processAppUsage得到最终的电量使用情况,我们重点关注和蓝牙有关的逻辑:

private void processAppUsage(SparseArray asUsers) {
        final boolean forAllUsers = (asUsers.get(UserHandle.USER_ALL) != null);
        mStatsPeriod = mTypeBatteryRealtimeUs;
        BatterySipper osSipper = null;
        final SparseArray uidStats = mStats.getUidStats();
        final int NU = uidStats.size();
        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, mRawRealtimeUs, mRawUptimeUs, mStatsType);
            mWakelockPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
            mMobileRadioPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
            mWifiPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
            mBluetoothPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
            mSensorPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
            mCameraPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
            mFlashlightPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);

            final double totalPower = app.sumPower();
            if (DEBUG && totalPower != 0) {
                Log.d(TAG, String.format("UID %d: total power=%s", u.getUid(),
                        makemAh(totalPower)));
            }

            if (totalPower != 0 || u.getUid() == 0) {
                final int uid = app.getUid();
                final int userId = UserHandle.getUserId(uid);
                if (uid == Process.WIFI_UID) {
                    mWifiSippers.add(app);
                } else if (uid == Process.BLUETOOTH_UID) {
                    mBluetoothSippers.add(app);
                } else if (!forAllUsers && asUsers.get(userId) == null
                        && UserHandle.getAppId(uid) >= Process.FIRST_APPLICATION_UID) {
                    List list = mUserSippers.get(userId);
                    if (list == null) {
                        list = new ArrayList<>();
                        mUserSippers.put(userId, list);
                    }
                    list.add(app);
                } else {
                    mUsageList.add(app);
                }

                if (uid == 0) {
                    osSipper = app;
                }
            }
        }

    }
    private void addBluetoothUsage() {
        BatterySipper bs = new BatterySipper(BatterySipper.DrainType.BLUETOOTH, null, 0);
        mBluetoothPowerCalculator.calculateRemaining(bs, mStats, mRawRealtimeUs, mRawUptimeUs,
                mStatsType);
        aggregateSippers(bs, mBluetoothSippers, "Bluetooth");
        if (bs.totalPowerMah > 0) {
            mUsageList.add(bs);
        }
    }

    可以看出主要通过mBluetoothPowerCalculator的calculateRemaining和calculateApp方法来计算应用的蓝牙耗电值和蓝牙的硬件耗电值。

    (2)BluetoothPowerCalculator中的逻辑

public class BluetoothPowerCalculator extends PowerCalculator {
    private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
    private static final String TAG = "BluetoothPowerCalculator";
    private final double mIdleMa;
    private final double mRxMa;
    private final double mTxMa;
    private double mAppTotalPowerMah = 0;
    private long mAppTotalTimeMs = 0;

    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);
    }

    @Override
    public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
                             long rawUptimeUs, int statsType) {

        final BatteryStats.ControllerActivityCounter counter = u.getBluetoothControllerActivity();
        if (counter == null) {
            return;
        }

        final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(statsType);
        final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(statsType);
        final long txTimeMs = counter.getTxTimeCounters()[0].getCountLocked(statsType);
        final long totalTimeMs = idleTimeMs + txTimeMs + rxTimeMs;
        double powerMah = counter.getPowerCounter().getCountLocked(statsType)
                / (double)(1000*60*60);

        if (powerMah == 0) {
            powerMah = ((idleTimeMs * mIdleMa) + (rxTimeMs * mRxMa) + (txTimeMs * mTxMa))
                    / (1000*60*60);
        }

        app.bluetoothPowerMah = powerMah;
        app.bluetoothRunningTimeMs = totalTimeMs;
        app.btRxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_BT_RX_DATA, statsType);
        app.btTxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_BT_TX_DATA, statsType);

        mAppTotalPowerMah += powerMah;
        mAppTotalTimeMs += totalTimeMs;
    }

    @Override
    public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs,
                                   long rawUptimeUs, int statsType) {
        final BatteryStats.ControllerActivityCounter counter =
                stats.getBluetoothControllerActivity();

        final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(statsType);
        final long txTimeMs = counter.getTxTimeCounters()[0].getCountLocked(statsType);
        final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(statsType);
        final long totalTimeMs = idleTimeMs + txTimeMs + rxTimeMs;
        double powerMah = counter.getPowerCounter().getCountLocked(statsType)
                 / (double)(1000*60*60);

        if (powerMah == 0) {
            // Some devices do not report the power, so calculate it.
            powerMah = ((idleTimeMs * mIdleMa) + (rxTimeMs * mRxMa) + (txTimeMs * mTxMa))
                    / (1000*60*60);
        }

        // Subtract what the apps used, but clamp to 0.
        powerMah = Math.max(0, powerMah - mAppTotalPowerMah);

        if (DEBUG && powerMah != 0) {
            Log.d(TAG, "Bluetooth active: time=" + (totalTimeMs)
                    + " power=" + BatteryStatsHelper.makemAh(powerMah));
        }

        app.bluetoothPowerMah = powerMah;
        app.bluetoothRunningTimeMs = Math.max(0, totalTimeMs - mAppTotalTimeMs);
    }

    @Override
    public void reset() {
        mAppTotalPowerMah = 0;
        mAppTotalTimeMs = 0;
    }
}

    可以看出每个BatteryStats.Uid对象都可以通过getBluetoothControllerActivity方法获取到一个counter对象,这个对象可以获取到蓝牙的接收数据时长,发送数据时长,空闲时长,还可以获取到一个powerMah,如果这个powerMah为0,则根据上述三个时长计算耗电量。在BluetoothPowerCalculator的构造方法中,初始化了三个时长对应的电流,电流的值怎么获取的,代码很简单,直接说结论:PowerProfile中有一个map,PowerProfile通过读取power_profile.xml来更新这个map的内容,但是有个坑,PowerProfile定义了一个数组,如果,在power_profile.xml中没有找到该项定义,他就要去读取系统内置资源的值,这就是主要的坑:

        int[] configResIds = new int[]{
                com.android.internal.R.integer.config_bluetooth_idle_cur_ma,
                com.android.internal.R.integer.config_bluetooth_rx_cur_ma,
                com.android.internal.R.integer.config_bluetooth_tx_cur_ma,
                com.android.internal.R.integer.config_bluetooth_operating_voltage_mv,
                com.android.internal.R.integer.config_wifi_idle_receive_cur_ma,
                com.android.internal.R.integer.config_wifi_active_rx_cur_ma,
                com.android.internal.R.integer.config_wifi_tx_cur_ma,
                com.android.internal.R.integer.config_wifi_operating_voltage_mv,
        };

        String[] configResIdKeys = new String[]{
                POWER_BLUETOOTH_CONTROLLER_IDLE,
                POWER_BLUETOOTH_CONTROLLER_RX,
                POWER_BLUETOOTH_CONTROLLER_TX,
                POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE,
                POWER_WIFI_CONTROLLER_IDLE,
                POWER_WIFI_CONTROLLER_RX,
                POWER_WIFI_CONTROLLER_TX,
                POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE,
        };

    从上面的代码可以看出蓝牙电流的key值为

        mIdleMa = profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_IDLE);
        mRxMa = profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_RX);
        mTxMa = profile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_TX);

    而对应的字符串为

    public static final String POWER_BLUETOOTH_CONTROLLER_IDLE = "bluetooth.controller.idle";
    public static final String POWER_BLUETOOTH_CONTROLLER_RX = "bluetooth.controller.rx";
    public static final String POWER_BLUETOOTH_CONTROLLER_TX = "bluetooth.controller.tx";

    可以看出,已经和bluetooth.active,bluetooth.on没有关系了,这就是为什么改这两个没有用的原因,那为什么蓝牙的耗电量极其之大呢?由上面的逻辑可以看出来,主要根据三个时常和一个powerMah,必然这几个值有的错了。我们找找,这几个值是在哪更新的,最终我们发现了问题:

        final double opVolt = mPowerProfile.getAveragePower(
                PowerProfile.POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE) / 1000.0;
        if (opVolt != 0) {
            // We store the power drain as mAms.
            mBluetoothActivity.getPowerCounter().addCountLocked(
                    (long) (info.getControllerEnergyUsed() / opVolt));
        }
    可以看到上述代码获取了PowerProfile中的电压值,powerMah的值为所消耗的能量除以电压值,结果发现我们的电压值为3,正常的一般为3300,意味着powerMah大了1100倍,这就是蓝牙为什么耗电量极其大的原因


    

你可能感兴趣的:(Android N 电量使用情况之蓝牙耗电量)