Convert WifiPowerCalculator to work with BatteryUsageStats
Also, combine WifePowerCalculator and WifiPowerEstimator: they have a lot more in common than different. Bug: 158137862 Test: atest WifiPowerCalculatorTest Change-Id: Ib937102043a1fe04941ecfd09b5077b14058e6ef
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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<UidBatteryConsumer.Builder> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<BatterySipper> sippers, BatteryStats batteryStats,
|
||||
long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> 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;
|
||||
}
|
||||
}
|
||||
@@ -71,6 +71,7 @@ import org.junit.runners.Suite;
|
||||
UserPowerCalculatorTest.class,
|
||||
VideoPowerCalculatorTest.class,
|
||||
WakelockPowerCalculatorTest.class,
|
||||
WifiPowerCalculatorTest.class,
|
||||
|
||||
com.android.internal.power.MeasuredEnergyStatsTest.class
|
||||
})
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user