diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java index c6efaace76d78..2b6f336848c38 100644 --- a/core/java/android/os/BatteryConsumer.java +++ b/core/java/android/os/BatteryConsumer.java @@ -47,6 +47,7 @@ public abstract class BatteryConsumer { POWER_COMPONENT_SYSTEM_SERVICES, POWER_COMPONENT_SENSORS, POWER_COMPONENT_GNSS, + POWER_COMPONENT_WIFI, POWER_COMPONENT_WAKELOCK, POWER_COMPONENT_SCREEN, POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS, @@ -66,6 +67,7 @@ public abstract class BatteryConsumer { public static final int POWER_COMPONENT_MOBILE_RADIO = 8; public static final int POWER_COMPONENT_SENSORS = 9; public static final int POWER_COMPONENT_GNSS = 10; + public static final int POWER_COMPONENT_WIFI = 11; public static final int POWER_COMPONENT_WAKELOCK = 12; public static final int POWER_COMPONENT_SCREEN = 13; // Power that is re-attributed to other battery consumers. For example, for System Server @@ -94,6 +96,7 @@ public abstract class BatteryConsumer { TIME_COMPONENT_MOBILE_RADIO, TIME_COMPONENT_SENSORS, TIME_COMPONENT_GNSS, + TIME_COMPONENT_WIFI, TIME_COMPONENT_WAKELOCK, TIME_COMPONENT_SCREEN, }) @@ -112,6 +115,7 @@ public abstract class BatteryConsumer { public static final int TIME_COMPONENT_MOBILE_RADIO = 8; public static final int TIME_COMPONENT_SENSORS = 9; public static final int TIME_COMPONENT_GNSS = 10; + public static final int TIME_COMPONENT_WIFI = 11; public static final int TIME_COMPONENT_WAKELOCK = 12; public static final int TIME_COMPONENT_SCREEN = 13; diff --git a/core/java/com/android/internal/os/UsageBasedPowerEstimator.java b/core/java/com/android/internal/os/UsageBasedPowerEstimator.java index 5910b61f0f6ff..fd5201431eabd 100644 --- a/core/java/com/android/internal/os/UsageBasedPowerEstimator.java +++ b/core/java/com/android/internal/os/UsageBasedPowerEstimator.java @@ -32,6 +32,10 @@ public class UsageBasedPowerEstimator { mAveragePowerMahPerMs = averagePowerMilliAmp / MILLIS_IN_HOUR; } + public boolean isSupported() { + return mAveragePowerMahPerMs != 0; + } + /** * Given a {@link BatteryStats.Timer}, returns the accumulated duration. */ diff --git a/core/java/com/android/internal/os/WifiPowerCalculator.java b/core/java/com/android/internal/os/WifiPowerCalculator.java index 63763f76c6ed0..98f613fc1c40c 100644 --- a/core/java/com/android/internal/os/WifiPowerCalculator.java +++ b/core/java/com/android/internal/os/WifiPowerCalculator.java @@ -15,8 +15,13 @@ */ 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.SystemBatteryConsumer; +import android.os.UidBatteryConsumer; import android.os.UserHandle; import android.util.Log; import android.util.SparseArray; @@ -30,25 +35,93 @@ import java.util.List; public class WifiPowerCalculator extends PowerCalculator { private static final boolean DEBUG = BatteryStatsHelper.DEBUG; private static final String TAG = "WifiPowerCalculator"; - private final double mIdleCurrentMa; - private final double mTxCurrentMa; - private final double mRxCurrentMa; - private final PowerProfile mPowerProfile; + 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 double mTotalAppPowerDrain = 0; - private long mTotalAppRunningTime = 0; - private WifiPowerEstimator mWifiPowerEstimator; - private boolean mHasWifiPowerReporting; + 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) { - mPowerProfile = 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)); - mIdleCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE); - mTxCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX); - mRxCurrentMa = profile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX); + mWifiPowerPerPacket = getWifiPowerPerPacket(profile); - mHasWifiPowerController = mIdleCurrentMa != 0 && mTxCurrentMa != 0 && mRxCurrentMa != 0; - mWifiPowerEstimator = new WifiPowerEstimator(mPowerProfile); + mHasWifiPowerController = + mIdlePowerEstimator.isSupported() && mTxPowerEstimator.isSupported() + && mRxPowerEstimator.isSupported(); + } + + @Override + public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, + long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { + + // batteryStats.hasWifiActivityReporting can change if we get energy data at a later point, + // so always check this field. + final boolean hasWifiPowerReporting = + mHasWifiPowerController && batteryStats.hasWifiActivityReporting(); + + final SystemBatteryConsumer.Builder systemBatteryConsumerBuilder = + builder.getOrCreateSystemBatteryConsumerBuilder( + SystemBatteryConsumer.DRAIN_TYPE_WIFI); + + 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); + calculateApp(powerDurationAndTraffic, app.getBatteryStatsUid(), rawRealtimeUs, + BatteryStats.STATS_SINCE_CHARGED, + hasWifiPowerReporting); + + totalAppDurationMs += powerDurationAndTraffic.durationMs; + totalAppPowerMah += powerDurationAndTraffic.powerMah; + + app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI, + powerDurationAndTraffic.durationMs); + app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI, + powerDurationAndTraffic.powerMah); + + if (app.getUid() == Process.WIFI_UID) { + systemBatteryConsumerBuilder.addUidBatteryConsumer(app); + app.excludeFromBatteryUsageStats(); + } + } + + calculateRemaining(powerDurationAndTraffic, batteryStats, rawRealtimeUs, + BatteryStats.STATS_SINCE_CHARGED, + hasWifiPowerReporting, totalAppDurationMs, totalAppPowerMah); + + systemBatteryConsumerBuilder + .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI, + powerDurationAndTraffic.durationMs) + .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI, + powerDurationAndTraffic.powerMah); } /** @@ -64,100 +137,151 @@ public class WifiPowerCalculator extends PowerCalculator { // batteryStats.hasWifiActivityReporting can change if we get energy data at a later point, // so always check this field. - mHasWifiPowerReporting = mHasWifiPowerController && batteryStats.hasWifiActivityReporting(); + final boolean hasWifiPowerReporting = + mHasWifiPowerController && batteryStats.hasWifiActivityReporting(); - super.calculate(sippers, batteryStats, rawRealtimeUs, rawUptimeUs, statsType, asUsers); - - BatterySipper bs = new BatterySipper(BatterySipper.DrainType.WIFI, null, 0); - calculateRemaining(bs, batteryStats, rawRealtimeUs, rawUptimeUs, statsType); + 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--) { - BatterySipper app = sippers.get(i); - 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 BatterySipper app = sippers.get(i); + if (app.drainType == BatterySipper.DrainType.APP) { + calculateApp(powerDurationAndTraffic, app.uidObj, rawRealtimeUs, statsType, + hasWifiPowerReporting); + + 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); + } } } + + calculateRemaining(powerDurationAndTraffic, batteryStats, rawRealtimeUs, statsType, + hasWifiPowerReporting, totalAppDurationMs, totalAppPowerMah); + + bs.wifiRunningTimeMs += powerDurationAndTraffic.durationMs; + bs.wifiPowerMah += powerDurationAndTraffic.powerMah; + if (bs.sumPower() > 0) { sippers.add(bs); } } - @Override - protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, - long rawUptimeUs, int statsType) { - if (!mHasWifiPowerReporting) { - mWifiPowerEstimator.calculateApp(app, u, rawRealtimeUs, rawUptimeUs, statsType); - return; - } - - final BatteryStats.ControllerActivityCounter counter = u.getWifiControllerActivity(); - if (counter == null) { - return; - } - - final long idleTime = counter.getIdleTimeCounter().getCountLocked(statsType); - final long txTime = counter.getTxTimeCounters()[0].getCountLocked(statsType); - final long rxTime = counter.getRxTimeCounter().getCountLocked(statsType); - app.wifiRunningTimeMs = idleTime + rxTime + txTime; - mTotalAppRunningTime += app.wifiRunningTimeMs; - - app.wifiPowerMah = - ((idleTime * mIdleCurrentMa) + (txTime * mTxCurrentMa) + (rxTime * mRxCurrentMa)) - / (1000 * 60 * 60); - mTotalAppPowerDrain += app.wifiPowerMah; - - app.wifiRxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_RX_DATA, + private void calculateApp(PowerDurationAndTraffic powerDurationAndTraffic, BatteryStats.Uid u, + long rawRealtimeUs, + int statsType, boolean hasWifiPowerReporting) { + powerDurationAndTraffic.wifiRxPackets = u.getNetworkActivityPackets( + BatteryStats.NETWORK_WIFI_RX_DATA, statsType); - app.wifiTxPackets = u.getNetworkActivityPackets(BatteryStats.NETWORK_WIFI_TX_DATA, + powerDurationAndTraffic.wifiTxPackets = u.getNetworkActivityPackets( + BatteryStats.NETWORK_WIFI_TX_DATA, statsType); - app.wifiRxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_RX_DATA, + powerDurationAndTraffic.wifiRxBytes = u.getNetworkActivityBytes( + BatteryStats.NETWORK_WIFI_RX_DATA, statsType); - app.wifiTxBytes = u.getNetworkActivityBytes(BatteryStats.NETWORK_WIFI_TX_DATA, + powerDurationAndTraffic.wifiTxBytes = u.getNetworkActivityBytes( + BatteryStats.NETWORK_WIFI_TX_DATA, statsType); - if (DEBUG && app.wifiPowerMah != 0) { - Log.d(TAG, "UID " + u.getUid() + ": idle=" + idleTime + "ms rx=" + rxTime + "ms tx=" + - txTime + "ms power=" + formatCharge(app.wifiPowerMah)); + if (hasWifiPowerReporting) { + 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; + powerDurationAndTraffic.powerMah = mIdlePowerEstimator.calculatePower(idleTime) + + mTxPowerEstimator.calculatePower(txTime) + + mRxPowerEstimator.calculatePower(rxTime); + + 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 double wifiPacketPower = ( + powerDurationAndTraffic.wifiRxPackets + powerDurationAndTraffic.wifiTxPackets) + * mWifiPowerPerPacket; + final long wifiRunningTime = u.getWifiRunningTime(rawRealtimeUs, statsType) / 1000; + final long wifiScanTimeMs = u.getWifiScanTime(rawRealtimeUs, statsType) / 1000; + long batchScanTimeMs = 0; + for (int bin = 0; bin < BatteryStats.Uid.NUM_WIFI_BATCHED_SCAN_BINS; bin++) { + batchScanTimeMs += u.getWifiBatchedScanTime(bin, rawRealtimeUs, statsType) / 1000; + } + + powerDurationAndTraffic.durationMs = wifiRunningTime; + powerDurationAndTraffic.powerMah = wifiPacketPower + + mPowerOnPowerEstimator.calculatePower(wifiRunningTime) + + mScanPowerEstimator.calculatePower(wifiScanTimeMs) + + mBatchScanPowerEstimator.calculatePower(batchScanTimeMs); + + if (DEBUG && powerDurationAndTraffic.powerMah != 0) { + Log.d(TAG, "UID " + u.getUid() + ": power=" + formatCharge( + powerDurationAndTraffic.powerMah)); + } } } - private void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs, - long rawUptimeUs, int statsType) { - if (!mHasWifiPowerReporting) { - mWifiPowerEstimator.calculateRemaining(app, stats, rawRealtimeUs, rawUptimeUs, - statsType); - return; + private void calculateRemaining(PowerDurationAndTraffic powerDurationAndTraffic, + BatteryStats stats, long rawRealtimeUs, + int statsType, boolean hasWifiPowerReporting, long totalAppDurationMs, + double totalAppPowerMah) { + long totalDurationMs; + double totalPowerMah; + if (hasWifiPowerReporting) { + 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; + + 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 = mIdlePowerEstimator.calculatePower(idleTimeMs) + + mTxPowerEstimator.calculatePower(txTimeMs) + + mRxPowerEstimator.calculatePower(rxTimeMs); + } + } else { + totalDurationMs = stats.getGlobalWifiRunningTime(rawRealtimeUs, statsType) / 1000; + totalPowerMah = mPowerOnPowerEstimator.calculatePower(totalDurationMs); } - 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); - - app.wifiRunningTimeMs = Math.max(0, - (idleTimeMs + rxTimeMs + txTimeMs) - mTotalAppRunningTime); - - double powerDrainMah = counter.getPowerCounter().getCountLocked(statsType) - / (double) (1000 * 60 * 60); - if (powerDrainMah == 0) { - // Some controllers do not report power drain, so we can calculate it here. - powerDrainMah = ((idleTimeMs * mIdleCurrentMa) + (txTimeMs * mTxCurrentMa) - + (rxTimeMs * mRxCurrentMa)) / (1000 * 60 * 60); - } - app.wifiPowerMah = Math.max(0, powerDrainMah - mTotalAppPowerDrain); + powerDurationAndTraffic.durationMs = Math.max(0, totalDurationMs - totalAppDurationMs); + powerDurationAndTraffic.powerMah = Math.max(0, totalPowerMah - totalAppPowerMah); if (DEBUG) { - Log.d(TAG, "left over WiFi power: " + formatCharge(app.wifiPowerMah)); + Log.d(TAG, "left over WiFi power: " + formatCharge(powerDurationAndTraffic.powerMah)); } } - @Override - public void reset() { - mTotalAppPowerDrain = 0; - mTotalAppRunningTime = 0; - mWifiPowerEstimator.reset(); + /** + * 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); } } diff --git a/core/java/com/android/internal/os/WifiPowerEstimator.java b/core/java/com/android/internal/os/WifiPowerEstimator.java deleted file mode 100644 index d0a105cfd9585..0000000000000 --- a/core/java/com/android/internal/os/WifiPowerEstimator.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * 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.BatteryStats; -import android.os.Process; -import android.os.UserHandle; -import android.util.Log; -import android.util.SparseArray; - -import java.util.List; - -/** - * Estimates WiFi power usage based on timers in BatteryStats. - */ -public class WifiPowerEstimator extends PowerCalculator { - private static final boolean DEBUG = BatteryStatsHelper.DEBUG; - private static final String TAG = "WifiPowerEstimator"; - private final double mWifiPowerPerPacket; - private final double mWifiPowerOn; - private final double mWifiPowerScan; - private final double mWifiPowerBatchScan; - private long mTotalAppWifiRunningTimeMs = 0; - - public WifiPowerEstimator(PowerProfile profile) { - mWifiPowerPerPacket = getWifiPowerPerPacket(profile); - mWifiPowerOn = profile.getAveragePower(PowerProfile.POWER_WIFI_ON); - mWifiPowerScan = profile.getAveragePower(PowerProfile.POWER_WIFI_SCAN); - mWifiPowerBatchScan = profile.getAveragePower(PowerProfile.POWER_WIFI_BATCHED_SCAN); - } - - @Override - public void calculate(List sippers, BatteryStats batteryStats, - long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray asUsers) { - super.calculate(sippers, batteryStats, rawRealtimeUs, rawUptimeUs, statsType, asUsers); - - BatterySipper bs = new BatterySipper(BatterySipper.DrainType.WIFI, null, 0); - calculateRemaining(bs, batteryStats, rawRealtimeUs, rawUptimeUs, statsType); - - for (int i = sippers.size() - 1; i >= 0; i--) { - BatterySipper app = sippers.get(i); - if (app.getUid() == Process.WIFI_UID) { - if (DEBUG) Log.d(TAG, "WiFi adding sipper " + app + ": cpu=" + app.cpuTimeMs); - app.isAggregated = true; - bs.add(app); - } - } - if (bs.sumPower() > 0) { - sippers.add(bs); - } - } - - /** - * Return estimated power per Wi-Fi packet in mAh/packet where 1 packet = 2 KB. - */ - private static double getWifiPowerPerPacket(PowerProfile profile) { - final long WIFI_BPS = 1000000; // TODO: Extract average bit rates from system - final double WIFI_POWER = profile.getAveragePower(PowerProfile.POWER_WIFI_ACTIVE) - / 3600; - return WIFI_POWER / (((double)WIFI_BPS) / 8 / 2048); - } - - @Override - protected 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; - if (DEBUG && app.wifiPowerMah != 0) { - Log.d(TAG, "UID " + u.getUid() + ": power=" + formatCharge(app.wifiPowerMah)); - } - } - - 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); - } - - @Override - public void reset() { - mTotalAppWifiRunningTimeMs = 0; - } -} diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java index b819d9edb2a80..a1beecc83f433 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java @@ -71,6 +71,7 @@ import org.junit.runners.Suite; UserPowerCalculatorTest.class, VideoPowerCalculatorTest.class, WakelockPowerCalculatorTest.class, + WifiPowerCalculatorTest.class, com.android.internal.power.MeasuredEnergyStatsTest.class }) diff --git a/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java new file mode 100644 index 0000000000000..e1005457c289f --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2021 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 static com.google.common.truth.Truth.assertThat; + +import android.net.NetworkCapabilities; +import android.net.NetworkStats; +import android.os.BatteryConsumer; +import android.os.Process; +import android.os.SystemBatteryConsumer; +import android.os.UidBatteryConsumer; +import android.os.WorkSource; +import android.os.connectivity.WifiActivityEnergyInfo; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class WifiPowerCalculatorTest { + private static final double PRECISION = 0.00001; + + private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42; + + @Rule + public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule() + .setAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE, 360.0) + .setAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX, 480.0) + .setAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX, 720.0) + .setAveragePower(PowerProfile.POWER_WIFI_ON, 360.0) + .setAveragePower(PowerProfile.POWER_WIFI_SCAN, 480.0) + .setAveragePower(PowerProfile.POWER_WIFI_BATCHED_SCAN, 720.0) + .setAveragePower(PowerProfile.POWER_WIFI_ACTIVE, 1080.0); + + @Test + public void testPowerControllerBasedModel() { + BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats(); + + batteryStats.noteNetworkInterfaceForTransports("wifi", + new int[]{NetworkCapabilities.TRANSPORT_WIFI}); + + NetworkStats networkStats = new NetworkStats(10000, 1) + .insertEntry("wifi", APP_UID, 0, 0, 1000, 100, 2000, 20, 100) + .insertEntry("wifi", Process.WIFI_UID, 0, 0, 1111, 111, 2222, 22, 111); + mStatsRule.setNetworkStats(networkStats); + + WifiActivityEnergyInfo energyInfo = new WifiActivityEnergyInfo(10000, + WifiActivityEnergyInfo.STACK_STATE_STATE_ACTIVE, 1000, 2000, 3000, 4000); + + batteryStats.updateWifiState(energyInfo, 1000, 1000); + + WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile()); + mStatsRule.apply(calculator); + + UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID); + assertThat(uidConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI)) + .isEqualTo(1423); + assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) + .isWithin(PRECISION).of(0.2214666); + + SystemBatteryConsumer systemConsumer = + mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_WIFI); + assertThat(systemConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI)) + .isEqualTo(5577); + assertThat(systemConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) + .isWithin(PRECISION).of(0.645200); + } + + @Test + public void testTimerBasedModel() { + BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats(); + + batteryStats.noteNetworkInterfaceForTransports("wifi", + new int[]{NetworkCapabilities.TRANSPORT_WIFI}); + + NetworkStats networkStats = new NetworkStats(10000, 1) + .insertEntry("wifi", APP_UID, 0, 0, 1000, 100, 2000, 20, 100) + .insertEntry("wifi", Process.WIFI_UID, 0, 0, 1111, 111, 2222, 22, 111); + mStatsRule.setNetworkStats(networkStats); + + batteryStats.noteWifiScanStartedLocked(APP_UID, 1000, 1000); + batteryStats.noteWifiScanStoppedLocked(APP_UID, 2000, 2000); + batteryStats.noteWifiRunningLocked(new WorkSource(APP_UID), 3000, 3000); + batteryStats.noteWifiStoppedLocked(new WorkSource(APP_UID), 4000, 4000); + batteryStats.noteWifiRunningLocked(new WorkSource(Process.WIFI_UID), 1111, 2222); + batteryStats.noteWifiStoppedLocked(new WorkSource(Process.WIFI_UID), 3333, 4444); + + // Don't pass WifiActivityEnergyInfo, making WifiPowerCalculator rely exclusively + // on the packet counts. + batteryStats.updateWifiState(/* energyInfo */ null, 1000, 1000); + + WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile()); + mStatsRule.apply(calculator); + + UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID); + assertThat(uidConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI)) + .isEqualTo(1000); + assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) + .isWithin(PRECISION).of(0.8231573); + + SystemBatteryConsumer systemConsumer = + mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_WIFI); + assertThat(systemConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WIFI)) + .isEqualTo(2222); + assertThat(systemConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI)) + .isWithin(PRECISION).of(0.8759216); + } +}