diff options
5 files changed, 234 insertions, 36 deletions
diff --git a/core/java/android/os/UidBatteryConsumer.java b/core/java/android/os/UidBatteryConsumer.java index bb40d905d481..dfa0c396485d 100644 --- a/core/java/android/os/UidBatteryConsumer.java +++ b/core/java/android/os/UidBatteryConsumer.java @@ -16,9 +16,13 @@ package android.os; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * Contains power consumption data attributed to a specific UID. * @@ -26,9 +30,37 @@ import android.annotation.Nullable; */ public final class UidBatteryConsumer extends BatteryConsumer implements Parcelable { + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + STATE_FOREGROUND, + STATE_BACKGROUND + }) + public @interface State { + } + + /** + * The state of an application when it is either running a foreground (top) activity + * or a foreground service. + */ + public static final int STATE_FOREGROUND = 0; + + /** + * The state of an application when it is running in the background, including the following + * states: + * + * {@link android.app.ActivityManager#PROCESS_STATE_IMPORTANT_BACKGROUND}, + * {@link android.app.ActivityManager#PROCESS_STATE_TRANSIENT_BACKGROUND}, + * {@link android.app.ActivityManager#PROCESS_STATE_BACKUP}, + * {@link android.app.ActivityManager#PROCESS_STATE_SERVICE}, + * {@link android.app.ActivityManager#PROCESS_STATE_RECEIVER}. + */ + public static final int STATE_BACKGROUND = 1; + private final int mUid; @Nullable private final String mPackageWithHighestDrain; + private final long mTimeInForegroundMs; + private final long mTimeInBackgroundMs; public int getUid() { return mUid; @@ -39,16 +71,33 @@ public final class UidBatteryConsumer extends BatteryConsumer implements Parcela return mPackageWithHighestDrain; } + /** + * Returns the amount of time in milliseconds this UID spent in the specified state. + */ + public long getTimeInStateMs(@State int state) { + switch (state) { + case STATE_BACKGROUND: + return mTimeInBackgroundMs; + case STATE_FOREGROUND: + return mTimeInForegroundMs; + } + return 0; + } + private UidBatteryConsumer(@NonNull Builder builder) { super(builder.mPowerComponentsBuilder.build()); mUid = builder.mUid; mPackageWithHighestDrain = builder.mPackageWithHighestDrain; + mTimeInForegroundMs = builder.mTimeInForegroundMs; + mTimeInBackgroundMs = builder.mTimeInBackgroundMs; } private UidBatteryConsumer(@NonNull Parcel source) { super(new PowerComponents(source)); mUid = source.readInt(); mPackageWithHighestDrain = source.readString(); + mTimeInForegroundMs = source.readLong(); + mTimeInBackgroundMs = source.readLong(); } /** @@ -59,6 +108,8 @@ public final class UidBatteryConsumer extends BatteryConsumer implements Parcela super.writeToParcel(dest, flags); dest.writeInt(mUid); dest.writeString(mPackageWithHighestDrain); + dest.writeLong(mTimeInForegroundMs); + dest.writeLong(mTimeInBackgroundMs); } @NonNull @@ -84,6 +135,8 @@ public final class UidBatteryConsumer extends BatteryConsumer implements Parcela private final BatteryStats.Uid mBatteryStatsUid; private final int mUid; private String mPackageWithHighestDrain; + public long mTimeInForegroundMs; + public long mTimeInBackgroundMs; private boolean mExcludeFromBatteryUsageStats; public Builder(int customPowerComponentCount, int customTimeComponentCount, @@ -113,6 +166,25 @@ public final class UidBatteryConsumer extends BatteryConsumer implements Parcela } /** + * Sets the duration, in milliseconds, that this UID was active in a particular state, + * such as foreground or background. + */ + @NonNull + public Builder setTimeInStateMs(@State int state, long timeInStateMs) { + switch (state) { + case STATE_FOREGROUND: + mTimeInForegroundMs = timeInStateMs; + break; + case STATE_BACKGROUND: + mTimeInBackgroundMs = timeInStateMs; + break; + default: + throw new IllegalArgumentException("Unsupported state: " + state); + } + return this; + } + + /** * Marks the UidBatteryConsumer for exclusion from the result set. */ public Builder excludeFromBatteryUsageStats() { diff --git a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java index eef9fa74e83a..0163acc295fb 100644 --- a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java +++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java @@ -22,10 +22,12 @@ import android.os.BatteryStats; import android.os.BatteryUsageStats; import android.os.BatteryUsageStatsQuery; import android.os.Bundle; -import android.os.SystemClock; +import android.os.UidBatteryConsumer; import android.os.UserHandle; import android.util.SparseArray; +import com.android.internal.annotations.VisibleForTesting; + import java.util.ArrayList; import java.util.List; @@ -85,7 +87,7 @@ public class BatteryUsageStatsProvider { } /** - * Returns a snapshot of battery attribution data. + * Returns snapshots of battery attribution data, one per supplied query. */ public List<BatteryUsageStats> getBatteryUsageStats(List<BatteryUsageStatsQuery> queries) { @@ -112,12 +114,19 @@ public class BatteryUsageStatsProvider { return results; } - private BatteryUsageStats getBatteryUsageStats(BatteryUsageStatsQuery query) { + /** + * Returns a snapshot of battery attribution data. + */ + @VisibleForTesting + public BatteryUsageStats getBatteryUsageStats(BatteryUsageStatsQuery query) { + final long realtimeUs = mStats.mClocks.elapsedRealtime() * 1000; + final long uptimeUs = mStats.mClocks.uptimeMillis() * 1000; + final long[] customMeasuredEnergiesMicroJoules = mStats.getCustomMeasuredEnergiesMicroJoules(); final int customPowerComponentCount = customMeasuredEnergiesMicroJoules != null - ? customMeasuredEnergiesMicroJoules.length - : 0; + ? customMeasuredEnergiesMicroJoules.length + : 0; // TODO(b/174186358): read extra time component number from configuration final int customTimeComponentCount = 0; @@ -128,12 +137,14 @@ public class BatteryUsageStatsProvider { SparseArray<? extends BatteryStats.Uid> uidStats = mStats.getUidStats(); for (int i = uidStats.size() - 1; i >= 0; i--) { - batteryUsageStatsBuilder.getOrCreateUidBatteryConsumerBuilder(uidStats.valueAt(i)); + final BatteryStats.Uid uid = uidStats.valueAt(i); + batteryUsageStatsBuilder.getOrCreateUidBatteryConsumerBuilder(uid) + .setTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND, + getProcessBackgroundTimeMs(uid, realtimeUs)) + .setTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND, + getProcessForegroundTimeMs(uid, realtimeUs)); } - final long realtimeUs = SystemClock.elapsedRealtime() * 1000; - final long uptimeUs = SystemClock.uptimeMillis() * 1000; - final List<PowerCalculator> powerCalculators = getPowerCalculators(); for (int i = 0, count = powerCalculators.size(); i < count; i++) { PowerCalculator powerCalculator = powerCalculators.get(i); @@ -143,4 +154,35 @@ public class BatteryUsageStatsProvider { return batteryUsageStatsBuilder.build(); } + + private long getProcessForegroundTimeMs(BatteryStats.Uid uid, long realtimeUs) { + final long topStateDurationMs = uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_TOP, + realtimeUs, BatteryStats.STATS_SINCE_CHARGED) / 1000; + + long foregroundActivityDurationMs = 0; + final BatteryStats.Timer foregroundActivityTimer = uid.getForegroundActivityTimer(); + if (foregroundActivityTimer != null) { + foregroundActivityDurationMs = foregroundActivityTimer.getTotalTimeLocked(realtimeUs, + BatteryStats.STATS_SINCE_CHARGED) / 1000; + } + + // Use the min value of STATE_TOP time and foreground activity time, since both of these + // times are imprecise + final long foregroundDurationMs = Math.min(topStateDurationMs, + foregroundActivityDurationMs); + + long foregroundServiceDurationMs = 0; + final BatteryStats.Timer foregroundServiceTimer = uid.getForegroundServiceTimer(); + if (foregroundServiceTimer != null) { + foregroundServiceDurationMs = foregroundServiceTimer.getTotalTimeLocked(realtimeUs, + BatteryStats.STATS_SINCE_CHARGED) / 1000; + } + + return foregroundDurationMs + foregroundServiceDurationMs; + } + + private long getProcessBackgroundTimeMs(BatteryStats.Uid uid, long realtimeUs) { + return uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_BACKGROUND, realtimeUs, + BatteryStats.STATS_SINCE_CHARGED) / 1000; + } } 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 b819d9edb2a8..434b7ef73118 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java @@ -40,6 +40,7 @@ import org.junit.runners.Suite; BatteryStatsTimeBaseTest.class, BatteryStatsTimerTest.class, BatteryStatsUidTest.class, + BatteryUsageStatsProviderTest.class, BatteryUsageStatsTest.class, BatteryStatsUserLifecycleTests.class, BluetoothPowerCalculatorTest.class, diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java new file mode 100644 index 000000000000..c4b7796b49cf --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java @@ -0,0 +1,76 @@ +/* + * 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.app.ActivityManager; +import android.content.Context; +import android.os.BatteryUsageStats; +import android.os.BatteryUsageStatsQuery; +import android.os.Process; +import android.os.UidBatteryConsumer; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.List; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class BatteryUsageStatsProviderTest { + private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42; + private static final long MINUTE_IN_MS = 60 * 1000; + + @Rule + public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule(); + + @Test + public void test_getBatteryUsageStats() { + BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats(); + + batteryStats.noteActivityResumedLocked(APP_UID, + 10 * MINUTE_IN_MS, 10 * MINUTE_IN_MS); + batteryStats.noteUidProcessStateLocked(APP_UID, ActivityManager.PROCESS_STATE_TOP, + 10 * MINUTE_IN_MS, 10 * MINUTE_IN_MS); + batteryStats.noteActivityPausedLocked(APP_UID, + 30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS); + batteryStats.noteUidProcessStateLocked(APP_UID, ActivityManager.PROCESS_STATE_SERVICE, + 30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS); + batteryStats.noteUidProcessStateLocked(APP_UID, ActivityManager.PROCESS_STATE_CACHED_EMPTY, + 40 * MINUTE_IN_MS, 40 * MINUTE_IN_MS); + + Context context = InstrumentationRegistry.getContext(); + BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context, batteryStats); + + final BatteryUsageStats batteryUsageStats = + provider.getBatteryUsageStats(BatteryUsageStatsQuery.DEFAULT); + + final List<UidBatteryConsumer> uidBatteryConsumers = + batteryUsageStats.getUidBatteryConsumers(); + final UidBatteryConsumer uidBatteryConsumer = uidBatteryConsumers.get(0); + assertThat(uidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND)) + .isEqualTo(20 * MINUTE_IN_MS); + assertThat(uidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND)) + .isEqualTo(10 * MINUTE_IN_MS); + } +} diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java index 355ac6dbcc00..9ef628848beb 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java @@ -66,33 +66,36 @@ public class BatteryUsageStatsTest { final MockBatteryStatsImpl batteryStats = new MockBatteryStatsImpl(clocks); final BatteryStatsImpl.Uid batteryStatsUid = batteryStats.getUidStatsLocked(2000); - final BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(1, 1); - builder.setDischargePercentage(20); - builder.setDischargedPowerRange(1000, 2000); - - final UidBatteryConsumer.Builder uidBatteryConsumerBuilder = - builder.getOrCreateUidBatteryConsumerBuilder(batteryStatsUid); - uidBatteryConsumerBuilder.setPackageWithHighestDrain("foo"); - uidBatteryConsumerBuilder.setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, 300); - uidBatteryConsumerBuilder.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 400); - uidBatteryConsumerBuilder.setConsumedPowerForCustomComponent( - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 500); - uidBatteryConsumerBuilder.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU, 600); - uidBatteryConsumerBuilder.setUsageDurationMillis( - BatteryConsumer.TIME_COMPONENT_CPU_FOREGROUND, 700); - uidBatteryConsumerBuilder.setUsageDurationForCustomComponentMillis( - BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID, 800); - - final SystemBatteryConsumer.Builder systemBatteryConsumerBuilder = - builder.getOrCreateSystemBatteryConsumerBuilder( - SystemBatteryConsumer.DRAIN_TYPE_CAMERA); - systemBatteryConsumerBuilder.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 10100); - systemBatteryConsumerBuilder.setConsumedPowerForCustomComponent( - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10200); - systemBatteryConsumerBuilder.setUsageDurationMillis( - BatteryConsumer.TIME_COMPONENT_CPU, 10300); - systemBatteryConsumerBuilder.setUsageDurationForCustomComponentMillis( - BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID, 10400); + final BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(1, 1) + .setDischargePercentage(20) + .setDischargedPowerRange(1000, 2000); + + builder.getOrCreateUidBatteryConsumerBuilder(batteryStatsUid) + .setPackageWithHighestDrain("foo") + .setTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND, 1000) + .setTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND, 2000) + .setConsumedPower( + BatteryConsumer.POWER_COMPONENT_USAGE, 300) + .setConsumedPower( + BatteryConsumer.POWER_COMPONENT_CPU, 400) + .setConsumedPowerForCustomComponent( + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 500) + .setUsageDurationMillis( + BatteryConsumer.TIME_COMPONENT_CPU, 600) + .setUsageDurationMillis( + BatteryConsumer.TIME_COMPONENT_CPU_FOREGROUND, 700) + .setUsageDurationForCustomComponentMillis( + BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID, 800); + + builder.getOrCreateSystemBatteryConsumerBuilder(SystemBatteryConsumer.DRAIN_TYPE_CAMERA) + .setConsumedPower( + BatteryConsumer.POWER_COMPONENT_CPU, 10100) + .setConsumedPowerForCustomComponent( + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10200) + .setUsageDurationMillis( + BatteryConsumer.TIME_COMPONENT_CPU, 10300) + .setUsageDurationForCustomComponentMillis( + BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID, 10400); return builder.build(); } @@ -108,6 +111,10 @@ public class BatteryUsageStatsTest { for (UidBatteryConsumer uidBatteryConsumer : uidBatteryConsumers) { if (uidBatteryConsumer.getUid() == 2000) { assertThat(uidBatteryConsumer.getPackageWithHighestDrain()).isEqualTo("foo"); + assertThat(uidBatteryConsumer.getTimeInStateMs( + UidBatteryConsumer.STATE_FOREGROUND)).isEqualTo(1000); + assertThat(uidBatteryConsumer.getTimeInStateMs( + UidBatteryConsumer.STATE_BACKGROUND)).isEqualTo(2000); assertThat(uidBatteryConsumer.getConsumedPower( BatteryConsumer.POWER_COMPONENT_USAGE)).isEqualTo(300); assertThat(uidBatteryConsumer.getConsumedPower( |