diff options
5 files changed, 175 insertions, 33 deletions
diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java index ce1a16da9e14..bf229e0b24df 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_WAKELOCK,              POWER_COMPONENT_SCREEN,              POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS,      }) @@ -65,6 +66,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_WAKELOCK = 12;      public static final int POWER_COMPONENT_SCREEN = 13;      // Power that is re-attributed to other battery consumers. For example, for System Server      // this represents the power attributed to apps requesting system services. @@ -92,6 +94,7 @@ public abstract class BatteryConsumer {              TIME_COMPONENT_MOBILE_RADIO,              TIME_COMPONENT_SENSORS,              TIME_COMPONENT_GNSS, +            TIME_COMPONENT_WAKELOCK,              TIME_COMPONENT_SCREEN,      })      @Retention(RetentionPolicy.SOURCE) @@ -109,6 +112,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_WAKELOCK = 12;      public static final int TIME_COMPONENT_SCREEN = 13;      public static final int TIME_COMPONENT_COUNT = 14; diff --git a/core/java/com/android/internal/os/WakelockPowerCalculator.java b/core/java/com/android/internal/os/WakelockPowerCalculator.java index 3f68597dc1cc..0f4767b859a3 100644 --- a/core/java/com/android/internal/os/WakelockPowerCalculator.java +++ b/core/java/com/android/internal/os/WakelockPowerCalculator.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.Process; +import android.os.UidBatteryConsumer;  import android.os.UserHandle;  import android.util.ArrayMap;  import android.util.Log; @@ -26,39 +31,93 @@ import java.util.List;  public class WakelockPowerCalculator extends PowerCalculator {      private static final String TAG = "WakelockPowerCalculator";      private static final boolean DEBUG = BatteryStatsHelper.DEBUG; -    private final double mPowerWakelock; -    private long mTotalAppWakelockTimeMs = 0; +    private final UsageBasedPowerEstimator mPowerEstimator; + +    private static class PowerAndDuration { +        public long durationMs; +        public double powerMah; +    }      public WakelockPowerCalculator(PowerProfile profile) { -        mPowerWakelock = profile.getAveragePower(PowerProfile.POWER_CPU_IDLE); +        mPowerEstimator = new UsageBasedPowerEstimator( +                profile.getAveragePower(PowerProfile.POWER_CPU_IDLE));      }      @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); +    public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats, +            long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { +        final PowerAndDuration result = new PowerAndDuration(); +        UidBatteryConsumer.Builder osBatteryConsumer = null; +        double osPowerMah = 0; +        long osDurationMs = 0; +        long totalAppDurationMs = 0; +        final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders = +                builder.getUidBatteryConsumerBuilders(); +        for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) { +            final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i); +            calculateApp(result, app.getBatteryStatsUid(), rawRealtimeUs, +                    BatteryStats.STATS_SINCE_CHARGED); +            app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WAKELOCK, result.durationMs) +                    .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK, result.powerMah); +            totalAppDurationMs += result.durationMs; + +            if (app.getUid() == Process.ROOT_UID) { +                osBatteryConsumer = app; +                osDurationMs = result.durationMs; +                osPowerMah = result.powerMah; +            } +        }          // The device has probably been awake for longer than the screen on          // time and application wake lock time would account for.  Assign          // this remainder to the OS, if possible. +        if (osBatteryConsumer != null) { +            calculateRemaining(result, batteryStats, rawRealtimeUs, rawUptimeUs, +                    BatteryStats.STATS_SINCE_CHARGED, osPowerMah, osDurationMs, totalAppDurationMs); +            osBatteryConsumer.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WAKELOCK, +                    result.durationMs) +                    .setConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK, result.powerMah); +        } +    } + +    @Override +    public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats, +            long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) { +        final PowerAndDuration result = new PowerAndDuration();          BatterySipper osSipper = null; +        double osPowerMah = 0; +        long osDurationMs = 0; +        long totalAppDurationMs = 0;          for (int i = sippers.size() - 1; i >= 0; i--) { -            BatterySipper app = sippers.get(i); -            if (app.getUid() == 0) { -                osSipper = app; -                break; +            final BatterySipper app = sippers.get(i); +            if (app.drainType == BatterySipper.DrainType.APP) { +                calculateApp(result, app.uidObj, rawRealtimeUs, statsType); +                app.wakeLockTimeMs = result.durationMs; +                app.wakeLockPowerMah = result.powerMah; +                totalAppDurationMs += result.durationMs; + +                if (app.getUid() == Process.ROOT_UID) { +                    osSipper = app; +                    osPowerMah = result.powerMah; +                    osDurationMs = result.durationMs; +                }              }          } +        // The device has probably been awake for longer than the screen on +        // time and application wake lock time would account for.  Assign +        // this remainder to the OS, if possible.          if (osSipper != null) { -            calculateRemaining(osSipper, batteryStats, rawRealtimeUs, rawUptimeUs, statsType); +            calculateRemaining(result, batteryStats, rawRealtimeUs, rawUptimeUs, statsType, +                    osPowerMah, osDurationMs, totalAppDurationMs); +            osSipper.wakeLockTimeMs = result.durationMs; +            osSipper.wakeLockPowerMah = result.powerMah;              osSipper.sumPower();          }      } -    @Override -    protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, -            long rawUptimeUs, int statsType) { +    private void calculateApp(PowerAndDuration result, BatteryStats.Uid u, long rawRealtimeUs, +            int statsType) {          long wakeLockTimeUs = 0;          final ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> wakelockStats =                  u.getWakelockStats(); @@ -73,34 +132,29 @@ public class WakelockPowerCalculator extends PowerCalculator {                  wakeLockTimeUs += timer.getTotalTimeLocked(rawRealtimeUs, statsType);              }          } -        app.wakeLockTimeMs = wakeLockTimeUs / 1000; // convert to millis -        mTotalAppWakelockTimeMs += app.wakeLockTimeMs; +        result.durationMs = wakeLockTimeUs / 1000; // convert to millis          // Add cost of holding a wake lock. -        app.wakeLockPowerMah = (app.wakeLockTimeMs * mPowerWakelock) / (1000 * 60 * 60); -        if (DEBUG && app.wakeLockPowerMah != 0) { -            Log.d(TAG, "UID " + u.getUid() + ": wake " + app.wakeLockTimeMs -                    + " power=" + formatCharge(app.wakeLockPowerMah)); +        result.powerMah = mPowerEstimator.calculatePower(result.durationMs); +        if (DEBUG && result.powerMah != 0) { +            Log.d(TAG, "UID " + u.getUid() + ": wake " + result.durationMs +                    + " power=" + formatCharge(result.powerMah));          }      } -    private void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs, -            long rawUptimeUs, int statsType) { -        long wakeTimeMillis = stats.getBatteryUptime(rawUptimeUs) / 1000; -        wakeTimeMillis -= mTotalAppWakelockTimeMs -                + (stats.getScreenOnTime(rawRealtimeUs, statsType) / 1000); +    private void calculateRemaining(PowerAndDuration result, BatteryStats stats, long rawRealtimeUs, +            long rawUptimeUs, int statsType, double osPowerMah, long osDurationMs, +            long totalAppDurationMs) { +        final long wakeTimeMillis = stats.getBatteryUptime(rawUptimeUs) / 1000 +                - stats.getScreenOnTime(rawRealtimeUs, statsType) / 1000 +                - totalAppDurationMs;          if (wakeTimeMillis > 0) { -            final double power = (wakeTimeMillis * mPowerWakelock) / (1000 * 60 * 60); +            final double power = mPowerEstimator.calculatePower(wakeTimeMillis);              if (DEBUG) {                  Log.d(TAG, "OS wakeLockTime " + wakeTimeMillis + " power " + formatCharge(power));              } -            app.wakeLockTimeMs += wakeTimeMillis; -            app.wakeLockPowerMah += power; +            result.durationMs = osDurationMs + wakeTimeMillis; +            result.powerMah = osPowerMah + power;          }      } - -    @Override -    public void reset() { -        mTotalAppWakelockTimeMs = 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 0fac4f79686a..2e6e0de8d0c2 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java @@ -68,6 +68,7 @@ import org.junit.runners.Suite;          SystemServicePowerCalculatorTest.class,          UserPowerCalculatorTest.class,          VideoPowerCalculatorTest.class, +        WakelockPowerCalculatorTest.class,          com.android.internal.power.MeasuredEnergyStatsTest.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 0ddc4c0035ce..5edd58fb3eb9 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java @@ -49,6 +49,7 @@ public class BatteryUsageStatsRule implements TestRule {      };      private BatteryUsageStats mBatteryUsageStats; +    private boolean mScreenOn;      public BatteryUsageStatsRule() {          Context context = InstrumentationRegistry.getContext(); @@ -97,6 +98,11 @@ public class BatteryUsageStatsRule implements TestRule {          return this;      } +    public BatteryUsageStatsRule startWithScreenOn(boolean screenOn) { +        mScreenOn = screenOn; +        return this; +    } +      public void setNetworkStats(NetworkStats networkStats) {          mBatteryStats.setNetworkStats(networkStats);      } @@ -115,6 +121,7 @@ public class BatteryUsageStatsRule implements TestRule {      private void noteOnBattery() {          mBatteryStats.setOnBatteryInternal(true);          mBatteryStats.getOnBatteryTimeBase().setRunning(true, 0, 0); +        mBatteryStats.getOnBatteryScreenOffTimeBase().setRunning(!mScreenOn, 0, 0);      }      public PowerProfile getPowerProfile() { diff --git a/core/tests/coretests/src/com/android/internal/os/WakelockPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/WakelockPowerCalculatorTest.java new file mode 100644 index 000000000000..4f71b438c6fa --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/WakelockPowerCalculatorTest.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.os.BatteryConsumer; +import android.os.BatteryStats; +import android.os.Process; +import android.os.UidBatteryConsumer; +import android.os.WorkSource; + +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 WakelockPowerCalculatorTest { +    private static final double PRECISION = 0.00001; + +    private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42; +    private static final int APP_PID = 3145; + +    @Rule +    public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule() +            .setAveragePower(PowerProfile.POWER_CPU_IDLE, 360.0); + +    @Test +    public void testTimerBasedModel() { +        mStatsRule.getUidStats(Process.ROOT_UID); + +        BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats(); + +        batteryStats.noteStartWakeFromSourceLocked(new WorkSource(APP_UID), APP_PID, "awake", "", +                BatteryStats.WAKE_TYPE_PARTIAL, true, 1000, 1000); +        batteryStats.noteStopWakeFromSourceLocked(new WorkSource(APP_UID), APP_PID, "awake", "", +                BatteryStats.WAKE_TYPE_PARTIAL, 2000, 2000); + +        mStatsRule.setTime(10_000_000, 6_000_000); + +        WakelockPowerCalculator calculator = +                new WakelockPowerCalculator(mStatsRule.getPowerProfile()); + +        mStatsRule.apply(calculator); + +        UidBatteryConsumer consumer = mStatsRule.getUidBatteryConsumer(APP_UID); +        assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WAKELOCK)) +                .isEqualTo(1000); +        assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK)) +                .isWithin(PRECISION).of(0.1); + +        UidBatteryConsumer osConsumer = mStatsRule.getUidBatteryConsumer(Process.ROOT_UID); +        assertThat(osConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WAKELOCK)) +                .isEqualTo(5000); +        assertThat(osConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK)) +                .isWithin(PRECISION).of(0.5); +    } +}  |