All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.com.android.internal.os.WifiPowerCalculator Maven / Gradle / Ivy

/*
 * Copyright (C) 2015 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.
 */
package com.android.internal.os;

import android.os.BatteryConsumer;
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.Process;
import android.os.UidBatteryConsumer;
import android.os.UserHandle;
import android.util.Log;
import android.util.SparseArray;

import java.util.List;

/**
 * WiFi power calculator for when BatteryStats supports energy reporting
 * from the WiFi controller.
 */
public class WifiPowerCalculator extends PowerCalculator {
    private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
    private static final String TAG = "WifiPowerCalculator";
    private final UsageBasedPowerEstimator mIdlePowerEstimator;
    private final UsageBasedPowerEstimator mTxPowerEstimator;
    private final UsageBasedPowerEstimator mRxPowerEstimator;
    private final UsageBasedPowerEstimator mPowerOnPowerEstimator;
    private final UsageBasedPowerEstimator mScanPowerEstimator;
    private final UsageBasedPowerEstimator mBatchScanPowerEstimator;
    private final boolean mHasWifiPowerController;
    private final double mWifiPowerPerPacket;

    private static class PowerDurationAndTraffic {
        public double powerMah;
        public long durationMs;

        public long wifiRxPackets;
        public long wifiTxPackets;
        public long wifiRxBytes;
        public long wifiTxBytes;
    }

    public WifiPowerCalculator(PowerProfile profile) {
        mPowerOnPowerEstimator = new UsageBasedPowerEstimator(
                profile.getAveragePower(PowerProfile.POWER_WIFI_ON));
        mScanPowerEstimator = new UsageBasedPowerEstimator(
                profile.getAveragePower(PowerProfile.POWER_WIFI_SCAN));
        mBatchScanPowerEstimator = new UsageBasedPowerEstimator(
                profile.getAveragePower(PowerProfile.POWER_WIFI_BATCHED_SCAN));
        mIdlePowerEstimator = new UsageBasedPowerEstimator(
                profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE));
        mTxPowerEstimator = new UsageBasedPowerEstimator(
                profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX));
        mRxPowerEstimator = new UsageBasedPowerEstimator(
                profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX));

        mWifiPowerPerPacket = getWifiPowerPerPacket(profile);

        mHasWifiPowerController =
                mIdlePowerEstimator.isSupported() && mTxPowerEstimator.isSupported()
                        && mRxPowerEstimator.isSupported();
    }

    @Override
    public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
            long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {

        long totalAppDurationMs = 0;
        double totalAppPowerMah = 0;
        final PowerDurationAndTraffic powerDurationAndTraffic = new PowerDurationAndTraffic();
        final SparseArray uidBatteryConsumerBuilders =
                builder.getUidBatteryConsumerBuilders();
        for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
            final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
            final long consumptionUC =
                    app.getBatteryStatsUid().getWifiMeasuredBatteryConsumptionUC();
            final int powerModel = getPowerModel(consumptionUC, query);
            calculateApp(powerDurationAndTraffic, app.getBatteryStatsUid(), powerModel,
                    rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED,
                    batteryStats.hasWifiActivityReporting(), consumptionUC);

            totalAppDurationMs += powerDurationAndTraffic.durationMs;
            totalAppPowerMah += powerDurationAndTraffic.powerMah;

            app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WIFI,
                    powerDurationAndTraffic.durationMs);
            app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI,
                    powerDurationAndTraffic.powerMah, powerModel);
        }

        final long consumptionUC = batteryStats.getWifiMeasuredBatteryConsumptionUC();
        final int powerModel = getPowerModel(consumptionUC, query);
        calculateRemaining(powerDurationAndTraffic, powerModel, batteryStats, rawRealtimeUs,
                BatteryStats.STATS_SINCE_CHARGED, batteryStats.hasWifiActivityReporting(),
                totalAppDurationMs, totalAppPowerMah, consumptionUC);

        builder.getAggregateBatteryConsumerBuilder(
                BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
                .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WIFI,
                        powerDurationAndTraffic.durationMs)
                .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI,
                        totalAppPowerMah + powerDurationAndTraffic.powerMah, powerModel);
        builder.getAggregateBatteryConsumerBuilder(
                BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
                .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI,
                        totalAppPowerMah, powerModel);
    }

    /**
     * We do per-app blaming of WiFi activity. If energy info is reported from the controller,
     * then only the WiFi process gets blamed here since we normalize power calculations and
     * assign all the power drain to apps. If energy info is not reported, we attribute the
     * difference between total running time of WiFi for all apps and the actual running time
     * of WiFi to the WiFi subsystem.
     */
    @Override
    public void calculate(List sippers, BatteryStats batteryStats,
            long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray asUsers) {

        final BatterySipper bs = new BatterySipper(BatterySipper.DrainType.WIFI, null, 0);

        long totalAppDurationMs = 0;
        double totalAppPowerMah = 0;
        final PowerDurationAndTraffic powerDurationAndTraffic = new PowerDurationAndTraffic();
        for (int i = sippers.size() - 1; i >= 0; i--) {
            final BatterySipper app = sippers.get(i);
            if (app.drainType == BatterySipper.DrainType.APP) {
                final long consumptionUC =
                        app.uidObj.getWifiMeasuredBatteryConsumptionUC();
                final int powerModel = getPowerModel(consumptionUC);
                calculateApp(powerDurationAndTraffic, app.uidObj, powerModel, rawRealtimeUs,
                        statsType, batteryStats.hasWifiActivityReporting(), consumptionUC);

                totalAppDurationMs += powerDurationAndTraffic.durationMs;
                totalAppPowerMah += powerDurationAndTraffic.powerMah;

                app.wifiPowerMah = powerDurationAndTraffic.powerMah;
                app.wifiRunningTimeMs = powerDurationAndTraffic.durationMs;
                app.wifiRxBytes = powerDurationAndTraffic.wifiRxBytes;
                app.wifiRxPackets = powerDurationAndTraffic.wifiRxPackets;
                app.wifiTxBytes = powerDurationAndTraffic.wifiTxBytes;
                app.wifiTxPackets = powerDurationAndTraffic.wifiTxPackets;
                if (app.getUid() == Process.WIFI_UID) {
                    if (DEBUG) Log.d(TAG, "WiFi adding sipper " + app + ": cpu=" + app.cpuTimeMs);
                    app.isAggregated = true;
                    bs.add(app);
                }
            }
        }

        final long consumptionUC = batteryStats.getWifiMeasuredBatteryConsumptionUC();
        final int powerModel = getPowerModel(consumptionUC);
        calculateRemaining(powerDurationAndTraffic, powerModel, batteryStats, rawRealtimeUs,
                statsType, batteryStats.hasWifiActivityReporting(), totalAppDurationMs,
                totalAppPowerMah, consumptionUC);

        bs.wifiRunningTimeMs += powerDurationAndTraffic.durationMs;
        bs.wifiPowerMah += powerDurationAndTraffic.powerMah;

        if (bs.sumPower() > 0) {
            sippers.add(bs);
        }
    }

    private void calculateApp(PowerDurationAndTraffic powerDurationAndTraffic,
            BatteryStats.Uid u, @BatteryConsumer.PowerModel int powerModel,
            long rawRealtimeUs, int statsType, boolean hasWifiActivityReporting,
            long consumptionUC) {

        powerDurationAndTraffic.wifiRxPackets = u.getNetworkActivityPackets(
                BatteryStats.NETWORK_WIFI_RX_DATA,
                statsType);
        powerDurationAndTraffic.wifiTxPackets = u.getNetworkActivityPackets(
                BatteryStats.NETWORK_WIFI_TX_DATA,
                statsType);
        powerDurationAndTraffic.wifiRxBytes = u.getNetworkActivityBytes(
                BatteryStats.NETWORK_WIFI_RX_DATA,
                statsType);
        powerDurationAndTraffic.wifiTxBytes = u.getNetworkActivityBytes(
                BatteryStats.NETWORK_WIFI_TX_DATA,
                statsType);

        if (powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) {
            powerDurationAndTraffic.powerMah = uCtoMah(consumptionUC);
        }

        if (hasWifiActivityReporting && mHasWifiPowerController) {
            final BatteryStats.ControllerActivityCounter counter = u.getWifiControllerActivity();
            if (counter != null) {
                final long idleTime = counter.getIdleTimeCounter().getCountLocked(statsType);
                final long txTime = counter.getTxTimeCounters()[0].getCountLocked(statsType);
                final long rxTime = counter.getRxTimeCounter().getCountLocked(statsType);

                powerDurationAndTraffic.durationMs = idleTime + rxTime + txTime;
                if (powerModel == BatteryConsumer.POWER_MODEL_POWER_PROFILE) {
                    powerDurationAndTraffic.powerMah
                            = calcPowerFromControllerDataMah(rxTime, txTime, idleTime);
                }

                if (DEBUG && powerDurationAndTraffic.powerMah != 0) {
                    Log.d(TAG, "UID " + u.getUid() + ": idle=" + idleTime + "ms rx=" + rxTime
                            + "ms tx=" + txTime + "ms power=" + formatCharge(
                            powerDurationAndTraffic.powerMah));
                }
            }
        } else {
            final long wifiRunningTime = u.getWifiRunningTime(rawRealtimeUs, statsType) / 1000;
            powerDurationAndTraffic.durationMs = wifiRunningTime;

            if (powerModel == BatteryConsumer.POWER_MODEL_POWER_PROFILE) {
                final long wifiScanTimeMs = u.getWifiScanTime(rawRealtimeUs, statsType) / 1000;
                long batchTimeMs = 0;
                for (int bin = 0; bin < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bin++) {
                    batchTimeMs += u.getWifiBatchedScanTime(bin, rawRealtimeUs, statsType) / 1000;
                }
                powerDurationAndTraffic.powerMah = calcPowerWithoutControllerDataMah(
                        powerDurationAndTraffic.wifiRxPackets,
                        powerDurationAndTraffic.wifiTxPackets,
                        wifiRunningTime, wifiScanTimeMs, batchTimeMs);
            }

            if (DEBUG && powerDurationAndTraffic.powerMah != 0) {
                Log.d(TAG, "UID " + u.getUid() + ": power=" + formatCharge(
                        powerDurationAndTraffic.powerMah));
            }
        }
    }

    private void calculateRemaining(PowerDurationAndTraffic powerDurationAndTraffic,
            @BatteryConsumer.PowerModel int powerModel, BatteryStats stats, long rawRealtimeUs,
            int statsType, boolean hasWifiActivityReporting, long totalAppDurationMs,
            double totalAppPowerMah, long consumptionUC) {

        long totalDurationMs;
        double totalPowerMah = 0;

        if (powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) {
            totalPowerMah = uCtoMah(consumptionUC);
        }

        if (hasWifiActivityReporting && mHasWifiPowerController) {
            final BatteryStats.ControllerActivityCounter counter =
                    stats.getWifiControllerActivity();

            final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(statsType);
            final long txTimeMs = counter.getTxTimeCounters()[0].getCountLocked(statsType);
            final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(statsType);

            totalDurationMs = idleTimeMs + rxTimeMs + txTimeMs;

            if (powerModel == BatteryConsumer.POWER_MODEL_POWER_PROFILE) {
                totalPowerMah = counter.getPowerCounter().getCountLocked(statsType)
                        / (double) (1000 * 60 * 60);
                if (totalPowerMah == 0) {
                    // Some controllers do not report power drain, so we can calculate it here.
                    totalPowerMah = calcPowerFromControllerDataMah(rxTimeMs, txTimeMs, idleTimeMs);
                }
            }
        } else {
            totalDurationMs = stats.getGlobalWifiRunningTime(rawRealtimeUs, statsType) / 1000;
            if (powerModel == BatteryConsumer.POWER_MODEL_POWER_PROFILE) {
                totalPowerMah = calcGlobalPowerWithoutControllerDataMah(totalDurationMs);
            }
        }

        powerDurationAndTraffic.durationMs = Math.max(0, totalDurationMs - totalAppDurationMs);
        powerDurationAndTraffic.powerMah = Math.max(0, totalPowerMah - totalAppPowerMah);

        if (DEBUG) {
            Log.d(TAG, "left over WiFi power: " + formatCharge(powerDurationAndTraffic.powerMah));
        }
    }

    /** Returns (global or uid) estimated wifi power used using WifiControllerActivity data. */
    public double calcPowerFromControllerDataMah(long rxTimeMs, long txTimeMs, long idleTimeMs) {
        return mRxPowerEstimator.calculatePower(rxTimeMs)
                + mTxPowerEstimator.calculatePower(txTimeMs)
                + mIdlePowerEstimator.calculatePower(idleTimeMs);
    }

    /** Returns per-uid estimated wifi power used using non-WifiControllerActivity data. */
    public double calcPowerWithoutControllerDataMah(long rxPackets, long txPackets,
            long wifiRunningTimeMs, long wifiScanTimeMs, long wifiBatchScanTimeMs) {
        return
                (rxPackets + txPackets) * mWifiPowerPerPacket
                + mPowerOnPowerEstimator.calculatePower(wifiRunningTimeMs)
                + mScanPowerEstimator.calculatePower(wifiScanTimeMs)
                + mBatchScanPowerEstimator.calculatePower(wifiBatchScanTimeMs);

    }

    /** Returns global estimated wifi power used using non-WifiControllerActivity data. */
    public double calcGlobalPowerWithoutControllerDataMah(long globalWifiRunningTimeMs) {
        return mPowerOnPowerEstimator.calculatePower(globalWifiRunningTimeMs);
    }

    /**
     * Return estimated power per Wi-Fi packet in mAh/packet where 1 packet = 2 KB.
     */
    private static double getWifiPowerPerPacket(PowerProfile profile) {
        // TODO(b/179392913): Extract average bit rates from system
        final long wifiBps = 1000000;
        final double averageWifiActivePower =
                profile.getAveragePower(PowerProfile.POWER_WIFI_ACTIVE) / 3600;
        return averageWifiActivePower / (((double) wifiBps) / 8 / 2048);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy