Merge "9/n: Add BiometricScheduler proto dump"
diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java
index ba29a15a..fdc3d65 100644
--- a/core/java/android/os/BatteryConsumer.java
+++ b/core/java/android/os/BatteryConsumer.java
@@ -43,6 +43,7 @@
POWER_COMPONENT_AUDIO,
POWER_COMPONENT_VIDEO,
POWER_COMPONENT_FLASHLIGHT,
+ POWER_COMPONENT_MOBILE_RADIO,
POWER_COMPONENT_SYSTEM_SERVICES,
})
@Retention(RetentionPolicy.SOURCE)
@@ -57,8 +58,9 @@
public static final int POWER_COMPONENT_VIDEO = 5;
public static final int POWER_COMPONENT_FLASHLIGHT = 6;
public static final int POWER_COMPONENT_SYSTEM_SERVICES = 7;
+ public static final int POWER_COMPONENT_MOBILE_RADIO = 8;
- public static final int POWER_COMPONENT_COUNT = 8;
+ public static final int POWER_COMPONENT_COUNT = 9;
public static final int FIRST_CUSTOM_POWER_COMPONENT_ID = 1000;
public static final int LAST_CUSTOM_POWER_COMPONENT_ID = 9999;
@@ -87,6 +89,7 @@
TIME_COMPONENT_BLUETOOTH,
TIME_COMPONENT_CAMERA,
TIME_COMPONENT_FLASHLIGHT,
+ TIME_COMPONENT_MOBILE_RADIO,
})
@Retention(RetentionPolicy.SOURCE)
public static @interface TimeComponent {
@@ -100,8 +103,9 @@
public static final int TIME_COMPONENT_AUDIO = 5;
public static final int TIME_COMPONENT_VIDEO = 6;
public static final int TIME_COMPONENT_FLASHLIGHT = 7;
+ public static final int TIME_COMPONENT_MOBILE_RADIO = 8;
- public static final int TIME_COMPONENT_COUNT = 8;
+ public static final int TIME_COMPONENT_COUNT = 9;
public static final int FIRST_CUSTOM_TIME_COMPONENT_ID = 1000;
public static final int LAST_CUSTOM_TIME_COMPONENT_ID = 9999;
diff --git a/core/java/android/os/SystemBatteryConsumer.java b/core/java/android/os/SystemBatteryConsumer.java
index 08e358f..49bf084 100644
--- a/core/java/android/os/SystemBatteryConsumer.java
+++ b/core/java/android/os/SystemBatteryConsumer.java
@@ -41,7 +41,7 @@
// Reserved: APP
DRAIN_TYPE_BLUETOOTH,
DRAIN_TYPE_CAMERA,
- DRAIN_TYPE_CELL,
+ DRAIN_TYPE_MOBILE_RADIO,
DRAIN_TYPE_FLASHLIGHT,
DRAIN_TYPE_IDLE,
DRAIN_TYPE_MEMORY,
@@ -59,7 +59,7 @@
public static final int DRAIN_TYPE_AMBIENT_DISPLAY = 0;
public static final int DRAIN_TYPE_BLUETOOTH = 2;
public static final int DRAIN_TYPE_CAMERA = 3;
- public static final int DRAIN_TYPE_CELL = 4;
+ public static final int DRAIN_TYPE_MOBILE_RADIO = 4;
public static final int DRAIN_TYPE_FLASHLIGHT = 5;
public static final int DRAIN_TYPE_IDLE = 6;
public static final int DRAIN_TYPE_MEMORY = 7;
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index f105320..ee3c12c 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -860,14 +860,11 @@
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
protected int mScreenState = Display.STATE_UNKNOWN;
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
- protected StopwatchTimer mScreenOnTimer;
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
- protected StopwatchTimer mScreenDozeTimer;
+ StopwatchTimer mScreenOnTimer;
+ StopwatchTimer mScreenDozeTimer;
int mScreenBrightnessBin = -1;
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
- protected final StopwatchTimer[] mScreenBrightnessTimer =
+ final StopwatchTimer[] mScreenBrightnessTimer =
new StopwatchTimer[NUM_SCREEN_BRIGHTNESS_BINS];
boolean mPretendScreenOff;
@@ -912,8 +909,7 @@
int mUsbDataState = USB_DATA_UNKNOWN;
int mGpsSignalQualityBin = -1;
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
- protected final StopwatchTimer[] mGpsSignalQualityTimer =
+ final StopwatchTimer[] mGpsSignalQualityTimer =
new StopwatchTimer[GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS];
int mPhoneSignalStrengthBin = -1;
@@ -929,6 +925,7 @@
final LongSamplingCounter[] mNetworkByteActivityCounters =
new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
+
final LongSamplingCounter[] mNetworkPacketActivityCounters =
new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
@@ -948,8 +945,7 @@
/**
* The Bluetooth controller activity (time in tx, rx, idle, and power consumed) for the device.
*/
- @VisibleForTesting
- protected ControllerActivityCounterImpl mBluetoothActivity;
+ ControllerActivityCounterImpl mBluetoothActivity;
/**
* The Modem controller activity (time in tx, rx, idle, and power consumed) for the device.
@@ -993,8 +989,7 @@
StopwatchTimer mWifiActiveTimer;
int mBluetoothScanNesting;
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
- protected StopwatchTimer mBluetoothScanTimer;
+ StopwatchTimer mBluetoothScanTimer;
int mMobileRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
long mMobileRadioActiveStartTimeMs;
@@ -10718,6 +10713,31 @@
mHandler = new MyHandler(handler.getLooper());
mConstants = new Constants(mHandler);
mStartCount++;
+ initTimersAndCounters();
+ mOnBattery = mOnBatteryInternal = false;
+ long uptimeUs = mClocks.uptimeMillis() * 1000;
+ long realtimeUs = mClocks.elapsedRealtime() * 1000;
+ initTimes(uptimeUs, realtimeUs);
+ mStartPlatformVersion = mEndPlatformVersion = Build.ID;
+ mDischargeStartLevel = 0;
+ mDischargeUnplugLevel = 0;
+ mDischargePlugLevel = -1;
+ mDischargeCurrentLevel = 0;
+ mCurrentBatteryLevel = 0;
+ initDischarge(realtimeUs);
+ clearHistoryLocked();
+ updateDailyDeadlineLocked();
+ mPlatformIdleStateCallback = cb;
+ mMeasuredEnergyRetriever = energyStatsCb;
+ mUserInfoProvider = userInfoProvider;
+
+ // Notify statsd that the system is initially not in doze.
+ mDeviceIdleMode = DEVICE_IDLE_MODE_OFF;
+ FrameworkStatsLog.write(FrameworkStatsLog.DEVICE_IDLE_MODE_STATE_CHANGED, mDeviceIdleMode);
+ }
+
+ @VisibleForTesting
+ protected void initTimersAndCounters() {
mScreenOnTimer = new StopwatchTimer(mClocks, null, -1, null, mOnBatteryTimeBase);
mScreenDozeTimer = new StopwatchTimer(mClocks, null, -1, null, mOnBatteryTimeBase);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
@@ -10789,26 +10809,6 @@
mDischargeLightDozeCounter = new LongSamplingCounter(mOnBatteryTimeBase);
mDischargeDeepDozeCounter = new LongSamplingCounter(mOnBatteryTimeBase);
mDischargeCounter = new LongSamplingCounter(mOnBatteryTimeBase);
- mOnBattery = mOnBatteryInternal = false;
- long uptimeUs = mClocks.uptimeMillis() * 1000;
- long realtimeUs = mClocks.elapsedRealtime() * 1000;
- initTimes(uptimeUs, realtimeUs);
- mStartPlatformVersion = mEndPlatformVersion = Build.ID;
- mDischargeStartLevel = 0;
- mDischargeUnplugLevel = 0;
- mDischargePlugLevel = -1;
- mDischargeCurrentLevel = 0;
- mCurrentBatteryLevel = 0;
- initDischarge(realtimeUs);
- clearHistoryLocked();
- updateDailyDeadlineLocked();
- mPlatformIdleStateCallback = cb;
- mMeasuredEnergyRetriever = energyStatsCb;
- mUserInfoProvider = userInfoProvider;
-
- // Notify statsd that the system is initially not in doze.
- mDeviceIdleMode = DEVICE_IDLE_MODE_OFF;
- FrameworkStatsLog.write(FrameworkStatsLog.DEVICE_IDLE_MODE_STATE_CHANGED, mDeviceIdleMode);
}
@UnsupportedAppUsage
@@ -11623,7 +11623,8 @@
@GuardedBy("mModemNetworkLock")
private NetworkStats mLastModemNetworkStats = new NetworkStats(0, -1);
- private NetworkStats readNetworkStatsLocked(String[] ifaces) {
+ @VisibleForTesting
+ protected NetworkStats readNetworkStatsLocked(String[] ifaces) {
try {
if (!ArrayUtils.isEmpty(ifaces)) {
INetworkStatsService statsService = INetworkStatsService.Stub.asInterface(
@@ -11922,7 +11923,7 @@
/**
* Distribute Cell radio energy info and network traffic to apps.
*/
- public void updateMobileRadioState(@Nullable final ModemActivityInfo activityInfo,
+ public void noteModemControllerActivity(@Nullable final ModemActivityInfo activityInfo,
long elapsedRealtimeMs, long uptimeMs) {
if (DEBUG_ENERGY) {
Slog.d(TAG, "Updating mobile radio stats with " + activityInfo);
diff --git a/core/java/com/android/internal/os/MobileRadioPowerCalculator.java b/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
index 790d2e5..e3bd64d 100644
--- a/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
+++ b/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
@@ -15,7 +15,12 @@
*/
package com.android.internal.os;
+import android.os.BatteryConsumer;
import android.os.BatteryStats;
+import android.os.BatteryUsageStats;
+import android.os.BatteryUsageStatsQuery;
+import android.os.SystemBatteryConsumer;
+import android.os.UidBatteryConsumer;
import android.os.UserHandle;
import android.telephony.CellSignalStrength;
import android.util.Log;
@@ -26,103 +31,146 @@
public class MobileRadioPowerCalculator extends PowerCalculator {
private static final String TAG = "MobRadioPowerCalculator";
private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
- private final double mPowerRadioOn;
- private final double[] mPowerBins = new double[CellSignalStrength.getNumSignalStrengthLevels()];
- private final double mPowerScan;
- private BatteryStats mStats;
- private long mTotalAppMobileActiveMs = 0;
- /**
- * Return estimated power (in mAs) of sending or receiving a packet with the mobile radio.
- */
- private double getMobilePowerPerPacket(long rawRealtimeUs, int statsType) {
- final long MOBILE_BPS = 200000; // TODO: Extract average bit rates from system
- final double MOBILE_POWER = mPowerRadioOn / 3600;
+ private static final int NUM_SIGNAL_STRENGTH_LEVELS =
+ CellSignalStrength.getNumSignalStrengthLevels();
- 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;
+ private final UsageBasedPowerEstimator mActivePowerEstimator;
+ private final UsageBasedPowerEstimator[] mIdlePowerEstimators =
+ new UsageBasedPowerEstimator[NUM_SIGNAL_STRENGTH_LEVELS];
+ private final UsageBasedPowerEstimator mScanPowerEstimator;
- 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);
+ private static class PowerAndDuration {
+ public long durationMs;
+ public double powerMah;
+ public long totalAppDurationMs;
+ public long signalDurationMs;
+ public long noCoverageDurationMs;
}
public MobileRadioPowerCalculator(PowerProfile profile) {
- double temp =
+ // Power consumption when radio is active
+ double powerRadioActiveMa =
profile.getAveragePowerOrDefault(PowerProfile.POWER_RADIO_ACTIVE, -1);
- if (temp != -1) {
- mPowerRadioOn = temp;
- } else {
+ if (powerRadioActiveMa == -1) {
double sum = 0;
sum += profile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_RX);
- for (int i = 0; i < mPowerBins.length; i++) {
+ for (int i = 0; i < NUM_SIGNAL_STRENGTH_LEVELS; i++) {
sum += profile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_TX, i);
}
- mPowerRadioOn = sum / (mPowerBins.length + 1);
+ powerRadioActiveMa = sum / (NUM_SIGNAL_STRENGTH_LEVELS + 1);
}
- temp = profile.getAveragePowerOrDefault(PowerProfile.POWER_RADIO_ON, -1);
- if (temp != -1) {
- for (int i = 0; i < mPowerBins.length; i++) {
- mPowerBins[i] = profile.getAveragePower(PowerProfile.POWER_RADIO_ON, i);
+ mActivePowerEstimator = new UsageBasedPowerEstimator(powerRadioActiveMa);
+
+ // Power consumption when radio is on, but idle
+ if (profile.getAveragePowerOrDefault(PowerProfile.POWER_RADIO_ON, -1) != -1) {
+ for (int i = 0; i < NUM_SIGNAL_STRENGTH_LEVELS; i++) {
+ mIdlePowerEstimators[i] = new UsageBasedPowerEstimator(
+ profile.getAveragePower(PowerProfile.POWER_RADIO_ON, i));
}
} else {
double idle = profile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_IDLE);
- mPowerBins[0] = idle * 25 / 180;
- for (int i = 1; i < mPowerBins.length; i++) {
- mPowerBins[i] = Math.max(1, idle / 256);
+
+ // Magical calculations preserved for historical compatibility
+ mIdlePowerEstimators[0] = new UsageBasedPowerEstimator(idle * 25 / 180);
+ for (int i = 1; i < NUM_SIGNAL_STRENGTH_LEVELS; i++) {
+ mIdlePowerEstimators[i] = new UsageBasedPowerEstimator(Math.max(1, idle / 256));
}
}
- mPowerScan = profile.getAveragePowerOrDefault(PowerProfile.POWER_RADIO_SCANNING, 0);
+ mScanPowerEstimator = new UsageBasedPowerEstimator(
+ profile.getAveragePowerOrDefault(PowerProfile.POWER_RADIO_SCANNING, 0));
+ }
+
+ @Override
+ public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
+ long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query,
+ SparseArray<UserHandle> asUsers) {
+
+ PowerAndDuration total = new PowerAndDuration();
+
+ final double powerPerPacketMah = getMobilePowerPerPacket(batteryStats, rawRealtimeUs,
+ BatteryStats.STATS_SINCE_CHARGED);
+ final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
+ builder.getUidBatteryConsumerBuilders();
+ for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
+ final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
+ final BatteryStats.Uid uid = app.getBatteryStatsUid();
+ calculateApp(app, uid, powerPerPacketMah, total);
+ }
+
+ calculateRemaining(total, batteryStats, rawRealtimeUs);
+
+ if (total.powerMah != 0) {
+ builder.getOrCreateSystemBatteryConsumerBuilder(
+ SystemBatteryConsumer.DRAIN_TYPE_MOBILE_RADIO)
+ .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_MOBILE_RADIO,
+ total.durationMs)
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, total.powerMah);
+ }
+ }
+
+ private void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u,
+ double powerPerPacketMah, PowerAndDuration total) {
+ final long radioActiveDurationMs = calculateDuration(u, BatteryStats.STATS_SINCE_CHARGED);
+ total.totalAppDurationMs += radioActiveDurationMs;
+
+ final double powerMah = calculatePower(u, powerPerPacketMah, radioActiveDurationMs);
+
+ app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_MOBILE_RADIO,
+ radioActiveDurationMs)
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO, powerMah);
}
@Override
public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {
- mStats = batteryStats;
- super.calculate(sippers, batteryStats, rawRealtimeUs, rawUptimeUs, statsType, asUsers);
+ final double mobilePowerPerPacket = getMobilePowerPerPacket(batteryStats, rawRealtimeUs,
+ statsType);
+ PowerAndDuration total = new PowerAndDuration();
+ for (int i = sippers.size() - 1; i >= 0; i--) {
+ final BatterySipper app = sippers.get(i);
+ if (app.drainType == BatterySipper.DrainType.APP) {
+ final BatteryStats.Uid u = app.uidObj;
+ calculateApp(app, u, statsType, mobilePowerPerPacket, total);
+ }
+ }
BatterySipper radio = new BatterySipper(BatterySipper.DrainType.CELL, null, 0);
- calculateRemaining(radio, mStats, rawRealtimeUs, rawUptimeUs, statsType);
- radio.sumPower();
+ calculateRemaining(total, batteryStats, rawRealtimeUs);
+ if (total.powerMah != 0) {
+ if (total.signalDurationMs != 0) {
+ radio.noCoveragePercent =
+ total.noCoverageDurationMs * 100.0 / total.signalDurationMs;
+ }
+ radio.mobileActive = total.durationMs;
+ radio.mobileActiveCount = batteryStats.getMobileRadioActiveUnknownCount(statsType);
+ radio.mobileRadioPowerMah = total.powerMah;
+ radio.sumPower();
+ }
if (radio.totalPowerMah > 0) {
sippers.add(radio);
}
}
- @Override
- protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
- long rawUptimeUs, int statsType) {
+ private void calculateApp(BatterySipper app, BatteryStats.Uid u, int statsType,
+ double powerPerPacketMah, PowerAndDuration total) {
+ app.mobileActive = calculateDuration(u, statsType);
+ app.mobileRadioPowerMah = calculatePower(u, powerPerPacketMah, app.mobileActive);
+ total.totalAppDurationMs += app.mobileActive;
+
// Add cost of mobile traffic.
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) {
- // We are tracking when the radio is up, so can use the active time to
- // determine power use.
- mTotalAppMobileActiveMs += app.mobileActive;
- app.mobileRadioPowerMah = (app.mobileActive * mPowerRadioOn) / (1000 * 60 * 60);
- } else {
- // We are not tracking when the radio is up, so must approximate power use
- // based on the number of packets.
- app.mobileRadioPowerMah = (app.mobileRxPackets + app.mobileTxPackets)
- * getMobilePowerPerPacket(rawRealtimeUs, statsType);
- }
if (DEBUG && app.mobileRadioPowerMah != 0) {
Log.d(TAG, "UID " + u.getUid() + ": mobile packets "
+ (app.mobileRxPackets + app.mobileTxPackets)
@@ -131,52 +179,80 @@
}
}
- private void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs,
- long rawUptimeUs, int statsType) {
- double power = 0;
+ private long calculateDuration(BatteryStats.Uid u, int statsType) {
+ return u.getMobileRadioActiveTime(statsType) / 1000;
+ }
+
+ private double calculatePower(BatteryStats.Uid u, double powerPerPacketMah,
+ long radioActiveDurationMs) {
+ if (radioActiveDurationMs > 0) {
+ // We are tracking when the radio is up, so can use the active time to
+ // determine power use.
+ return mActivePowerEstimator.calculatePower(radioActiveDurationMs);
+ } else {
+ // We are not tracking when the radio is up, so must approximate power use
+ // based on the number of packets.
+ final long mobileRxPackets = u.getNetworkActivityPackets(
+ BatteryStats.NETWORK_MOBILE_RX_DATA,
+ BatteryStats.STATS_SINCE_CHARGED);
+ final long mobileTxPackets = u.getNetworkActivityPackets(
+ BatteryStats.NETWORK_MOBILE_TX_DATA,
+ BatteryStats.STATS_SINCE_CHARGED);
+ return (mobileRxPackets + mobileTxPackets) * powerPerPacketMah;
+ }
+ }
+
+ private void calculateRemaining(MobileRadioPowerCalculator.PowerAndDuration total,
+ BatteryStats batteryStats, long rawRealtimeUs) {
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);
+ double powerMah = 0;
+ for (int i = 0; i < NUM_SIGNAL_STRENGTH_LEVELS; i++) {
+ long strengthTimeMs = batteryStats.getPhoneSignalStrengthTime(i, rawRealtimeUs,
+ BatteryStats.STATS_SINCE_CHARGED) / 1000;
+ final double p = mIdlePowerEstimators[i].calculatePower(strengthTimeMs);
if (DEBUG && p != 0) {
Log.d(TAG, "Cell strength #" + i + ": time=" + strengthTimeMs + " power="
+ formatCharge(p));
}
- power += p;
+ powerMah += p;
signalTimeMs += strengthTimeMs;
if (i == 0) {
- noCoverageTimeMs = strengthTimeMs;
+ total.noCoverageDurationMs = strengthTimeMs;
}
}
- final long scanningTimeMs = stats.getPhoneSignalScanningTime(rawRealtimeUs, statsType)
- / 1000;
- final double p = (scanningTimeMs * mPowerScan) / (60 * 60 * 1000);
+ final long scanningTimeMs = batteryStats.getPhoneSignalScanningTime(rawRealtimeUs,
+ BatteryStats.STATS_SINCE_CHARGED) / 1000;
+ final double p = mScanPowerEstimator.calculatePower(scanningTimeMs);
if (DEBUG && p != 0) {
Log.d(TAG, "Cell radio scanning: time=" + scanningTimeMs + " power=" + formatCharge(p));
}
- power += p;
- long radioActiveTimeMs = mStats.getMobileRadioActiveTime(rawRealtimeUs, statsType) / 1000;
- long remainingActiveTimeMs = radioActiveTimeMs - mTotalAppMobileActiveMs;
+ powerMah += p;
+ long radioActiveTimeMs = batteryStats.getMobileRadioActiveTime(rawRealtimeUs,
+ BatteryStats.STATS_SINCE_CHARGED) / 1000;
+ long remainingActiveTimeMs = radioActiveTimeMs - total.totalAppDurationMs;
if (remainingActiveTimeMs > 0) {
- power += (mPowerRadioOn * remainingActiveTimeMs) / (1000 * 60 * 60);
+ powerMah += mActivePowerEstimator.calculatePower(remainingActiveTimeMs);
}
-
- if (power != 0) {
- if (signalTimeMs != 0) {
- app.noCoveragePercent = noCoverageTimeMs * 100.0 / signalTimeMs;
- }
- app.mobileActive = remainingActiveTimeMs;
- app.mobileActiveCount = stats.getMobileRadioActiveUnknownCount(statsType);
- app.mobileRadioPowerMah = power;
- }
+ total.durationMs = radioActiveTimeMs;
+ total.powerMah = powerMah;
+ total.signalDurationMs = signalTimeMs;
}
- @Override
- public void reset() {
- mTotalAppMobileActiveMs = 0;
- mStats = null;
+ /**
+ * Return estimated power (in mAh) of sending or receiving a packet with the mobile radio.
+ */
+ private double getMobilePowerPerPacket(BatteryStats stats, long rawRealtimeUs, int statsType) {
+ final long radioDataUptimeMs =
+ stats.getMobileRadioActiveTime(rawRealtimeUs, statsType) / 1000;
+ final double mobilePower = mActivePowerEstimator.calculatePower(radioDataUptimeMs);
+
+ final long mobileRx = stats.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_RX_DATA,
+ statsType);
+ final long mobileTx = stats.getNetworkActivityPackets(BatteryStats.NETWORK_MOBILE_TX_DATA,
+ statsType);
+ final long mobilePackets = mobileRx + mobileTx;
+
+ return mobilePackets != 0 ? mobilePower / mobilePackets : 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 8ff318e..dc33253 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -58,6 +58,7 @@
KernelWakelockReaderTest.class,
LongSamplingCounterTest.class,
LongSamplingCounterArrayTest.class,
+ MobileRadioPowerCalculatorTest.class,
PowerCalculatorTest.class,
PowerProfileTest.class,
ScreenPowerCalculatorTest.class,
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
index 55f64f9..59534e4 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
@@ -16,10 +16,13 @@
package com.android.internal.os;
+import static org.mockito.ArgumentMatchers.anyDouble;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.Context;
+import android.net.NetworkStats;
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
@@ -32,6 +35,7 @@
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
+import org.mockito.stubbing.Answer;
public class BatteryUsageStatsRule implements TestRule {
private final PowerProfile mPowerProfile;
@@ -53,6 +57,14 @@
public BatteryUsageStatsRule setAveragePower(String key, double value) {
when(mPowerProfile.getAveragePower(key)).thenReturn(value);
+ when(mPowerProfile.getAveragePowerOrDefault(eq(key), anyDouble())).thenReturn(value);
+ return this;
+ }
+
+ public BatteryUsageStatsRule setAveragePowerUnspecified(String key) {
+ when(mPowerProfile.getAveragePower(key)).thenReturn(0.0);
+ when(mPowerProfile.getAveragePowerOrDefault(eq(key), anyDouble()))
+ .thenAnswer((Answer<Double>) invocation -> (Double) invocation.getArguments()[1]);
return this;
}
@@ -64,6 +76,10 @@
return this;
}
+ public void setNetworkStats(NetworkStats networkStats) {
+ mBatteryStats.setNetworkStats(networkStats);
+ }
+
@Override
public Statement apply(Statement base, Description description) {
return new Statement() {
@@ -76,6 +92,7 @@
}
private void noteOnBattery() {
+ mBatteryStats.setOnBatteryInternal(true);
mBatteryStats.getOnBatteryTimeBase().setRunning(true, 0, 0);
}
diff --git a/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
new file mode 100644
index 0000000..4230066
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
@@ -0,0 +1,108 @@
+/*
+ * 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 static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.net.ConnectivityManager;
+import android.net.NetworkStats;
+import android.os.BatteryConsumer;
+import android.os.Process;
+import android.os.SystemBatteryConsumer;
+import android.os.UidBatteryConsumer;
+import android.telephony.DataConnectionRealTimeInfo;
+import android.telephony.ModemActivityInfo;
+import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
+import android.telephony.TelephonyManager;
+
+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 MobileRadioPowerCalculatorTest {
+ 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()
+ .setAveragePowerUnspecified(PowerProfile.POWER_RADIO_ACTIVE)
+ .setAveragePowerUnspecified(PowerProfile.POWER_RADIO_ON)
+ .setAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_IDLE, 360.0)
+ .setAveragePower(PowerProfile.POWER_RADIO_SCANNING, 720.0)
+ .setAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_RX, 1440.0)
+ .setAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_TX,
+ new double[] {720.0, 1080.0, 1440.0, 1800.0, 2160.0});
+
+ @Test
+ public void testCounterBasedModel() {
+ BatteryStatsImpl stats = mStatsRule.getBatteryStats();
+
+ // Scan for a cell
+ stats.notePhoneStateLocked(ServiceState.STATE_OUT_OF_SERVICE,
+ TelephonyManager.SIM_STATE_READY,
+ 2000, 2000);
+
+ // Found a cell
+ stats.notePhoneStateLocked(ServiceState.STATE_IN_SERVICE, TelephonyManager.SIM_STATE_READY,
+ 5000, 5000);
+
+ // Note cell signal strength
+ SignalStrength signalStrength = mock(SignalStrength.class);
+ when(signalStrength.getLevel()).thenReturn(SignalStrength.SIGNAL_STRENGTH_MODERATE);
+ stats.notePhoneSignalStrengthLocked(signalStrength, 5000, 5000);
+
+ stats.noteMobileRadioPowerStateLocked(DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH,
+ 8_000_000_000L, APP_UID, 8000, 8000);
+
+ // Note established network
+ stats.noteNetworkInterfaceType("cellular", ConnectivityManager.TYPE_MOBILE);
+
+ // Note application network activity
+ NetworkStats networkStats = new NetworkStats(10000, 1)
+ .insertEntry("cellular", APP_UID, 0, 0, 1000, 100, 2000, 20, 100);
+ mStatsRule.setNetworkStats(networkStats);
+
+ ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000,
+ new int[] {100, 200, 300, 400, 500}, 600);
+ stats.noteModemControllerActivity(mai, 10000, 10000);
+
+ mStatsRule.setTime(12_000_000, 12_000_000);
+
+ MobileRadioPowerCalculator calculator =
+ new MobileRadioPowerCalculator(mStatsRule.getPowerProfile());
+
+ mStatsRule.apply(calculator);
+
+ SystemBatteryConsumer consumer =
+ mStatsRule.getSystemBatteryConsumer(SystemBatteryConsumer.DRAIN_TYPE_MOBILE_RADIO);
+ assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
+ .isWithin(PRECISION).of(1.44440);
+
+ UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID);
+ assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
+ .isWithin(PRECISION).of(0.8);
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
index fc23721..bf74c8d 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -16,7 +16,7 @@
package com.android.internal.os;
-import android.location.GnssSignalQuality;
+import android.net.NetworkStats;
import android.os.Handler;
import android.os.Looper;
import android.util.SparseIntArray;
@@ -38,26 +38,14 @@
public class MockBatteryStatsImpl extends BatteryStatsImpl {
public BatteryStatsImpl.Clocks clocks;
public boolean mForceOnBattery;
+ private NetworkStats mNetworkStats;
MockBatteryStatsImpl(Clocks clocks) {
super(clocks);
this.clocks = mClocks;
- mScreenOnTimer = new BatteryStatsImpl.StopwatchTimer(clocks, null, -1, null,
- mOnBatteryTimeBase);
- mScreenDozeTimer = new BatteryStatsImpl.StopwatchTimer(clocks, null, -1, null,
- mOnBatteryTimeBase);
- for (int i = 0; i < mScreenBrightnessTimer.length; i++) {
- mScreenBrightnessTimer[i] = new BatteryStatsImpl.StopwatchTimer(clocks, null, -1, null,
- mOnBatteryTimeBase);
- }
- mBluetoothScanTimer = new StopwatchTimer(mClocks, null, -14, null, mOnBatteryTimeBase);
- mBluetoothActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase, 1);
- setExternalStatsSyncLocked(new DummyExternalStatsSync());
+ initTimersAndCounters();
- for (int i = 0; i < GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS; i++) {
- mGpsSignalQualityTimer[i] = new StopwatchTimer(clocks, null, -1000 - i, null,
- mOnBatteryTimeBase);
- }
+ setExternalStatsSyncLocked(new DummyExternalStatsSync());
final boolean[] supportedBuckets = new boolean[MeasuredEnergyStats.NUMBER_ENERGY_BUCKETS];
Arrays.fill(supportedBuckets, true);
@@ -106,6 +94,16 @@
return getUidStatsLocked(uid).mOnBatteryScreenOffBackgroundTimeBase;
}
+ public MockBatteryStatsImpl setNetworkStats(NetworkStats networkStats) {
+ mNetworkStats = networkStats;
+ return this;
+ }
+
+ @Override
+ protected NetworkStats readNetworkStatsLocked(String[] ifaces) {
+ return mNetworkStats;
+ }
+
public MockBatteryStatsImpl setPowerProfile(PowerProfile powerProfile) {
mPowerProfile = powerProfile;
return this;
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index ada7eea..a3fac05 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -593,7 +593,7 @@
}
if (modemInfo != null) {
- mStats.updateMobileRadioState(modemInfo, elapsedRealtime, uptime);
+ mStats.noteModemControllerActivity(modemInfo, elapsedRealtime, uptime);
}
if (updateFlags == UPDATE_ALL) {
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index cc23e233..b1cbb4a 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -1853,7 +1853,7 @@
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
mHandler.post(() -> {
- mStats.updateMobileRadioState(info, elapsedRealtime, uptime);
+ mStats.noteModemControllerActivity(info, elapsedRealtime, uptime);
});
}
}