Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 92d9361f authored by Kuan Wang's avatar Kuan Wang
Browse files

Enroll aggregated wakelock data (including background data) into

BatteryStatsManager.getWakeLockStats() API.

Bug: 320881704
Test: atest
Change-Id: I3cafa500fa0e203b72c603d803bf988a41dd6023
parent a7bbb2f8
Loading
Loading
Loading
Loading
+142 −30
Original line number Diff line number Diff line
@@ -23,17 +23,21 @@ import java.util.List;

/**
 * Snapshot of wake lock stats.
 *
 * @hide
 */
@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class WakeLockStats implements Parcelable {

    /** @hide */
    public static class WakeLock {
        public final int uid;
        @NonNull
        public final String name;
    public static class WakeLockData {

        public static final WakeLockData EMPTY = new WakeLockData(
                /* timesAcquired= */ 0, /* totalTimeHeldMs= */ 0, /* timeHeldMs= */ 0);

        /** How many times this wakelock has been acquired. */
        public final int timesAcquired;

        /** Time in milliseconds that the lock has been held in total. */
        public final long totalTimeHeldMs;

        /**
@@ -41,48 +45,133 @@ public final class WakeLockStats implements Parcelable {
         */
        public final long timeHeldMs;

        public WakeLock(int uid, @NonNull String name, int timesAcquired, long totalTimeHeldMs,
                long timeHeldMs) {
            this.uid = uid;
            this.name = name;
        public WakeLockData(int timesAcquired, long totalTimeHeldMs, long timeHeldMs) {
            this.timesAcquired = timesAcquired;
            this.totalTimeHeldMs = totalTimeHeldMs;
            this.timeHeldMs = timeHeldMs;
        }

        private WakeLock(Parcel in) {
            uid = in.readInt();
            name = in.readString();
        /**
         * Whether the fields are able to construct a valid wakelock.
         */
        public boolean isDataValid() {
            final boolean isDataReasonable = timesAcquired > 0
                    && totalTimeHeldMs > 0
                    && timeHeldMs >= 0
                    && totalTimeHeldMs >= timeHeldMs;
            return isEmpty() || isDataReasonable;
        }

        private boolean isEmpty() {
            return timesAcquired == 0 && totalTimeHeldMs == 0 && timeHeldMs == 0;
        }

        private WakeLockData(Parcel in) {
            timesAcquired = in.readInt();
            totalTimeHeldMs = in.readLong();
            timeHeldMs = in.readLong();
        }

        private void writeToParcel(Parcel out) {
            out.writeInt(uid);
            out.writeString(name);
            out.writeInt(timesAcquired);
            out.writeLong(totalTimeHeldMs);
            out.writeLong(timeHeldMs);
        }

        @Override
        public String toString() {
            return "WakeLockData{"
                + "timesAcquired="
                + timesAcquired
                + ", totalTimeHeldMs="
                + totalTimeHeldMs
                + ", timeHeldMs="
                + timeHeldMs
                + "}";
        }
    }

    /** @hide */
    public static class WakeLock {

        public static final String NAME_AGGREGATED = "wakelockstats_aggregated";

        public final int uid;
        @NonNull public final String name;
        public final boolean isAggregated;

        /** Wakelock data on both foreground and background. */
        @NonNull public final WakeLockData totalWakeLockData;

        /** Wakelock data on background. */
        @NonNull public final WakeLockData backgroundWakeLockData;

        public WakeLock(
                int uid,
                @NonNull String name,
                boolean isAggregated,
                @NonNull WakeLockData totalWakeLockData,
                @NonNull WakeLockData backgroundWakeLockData) {
            this.uid = uid;
            this.name = name;
            this.isAggregated = isAggregated;
            this.totalWakeLockData = totalWakeLockData;
            this.backgroundWakeLockData = backgroundWakeLockData;
        }

        /** Whether the combination of total and background wakelock data is invalid. */
        public static boolean isDataValid(
                WakeLockData totalWakeLockData, WakeLockData backgroundWakeLockData) {
            return totalWakeLockData.totalTimeHeldMs > 0
                && totalWakeLockData.isDataValid()
                && backgroundWakeLockData.isDataValid()
                && totalWakeLockData.timesAcquired >= backgroundWakeLockData.timesAcquired
                && totalWakeLockData.totalTimeHeldMs >= backgroundWakeLockData.totalTimeHeldMs
                && totalWakeLockData.timeHeldMs >= backgroundWakeLockData.timeHeldMs;
        }

        private WakeLock(Parcel in) {
            uid = in.readInt();
            name = in.readString();
            isAggregated = in.readBoolean();
            totalWakeLockData = new WakeLockData(in);
            backgroundWakeLockData = new WakeLockData(in);
        }

        private void writeToParcel(Parcel out) {
            out.writeInt(uid);
            out.writeString(name);
            out.writeBoolean(isAggregated);
            totalWakeLockData.writeToParcel(out);
            backgroundWakeLockData.writeToParcel(out);
        }

        @Override
        public String toString() {
            return "WakeLock{"
                    + "uid=" + uid
                    + ", name='" + name + '\''
                    + ", timesAcquired=" + timesAcquired
                    + ", totalTimeHeldMs=" + totalTimeHeldMs
                    + ", timeHeldMs=" + timeHeldMs
                + "uid="
                + uid
                + ", name='"
                + name
                + '\''
                + ", isAggregated="
                + isAggregated
                + ", totalWakeLockData="
                + totalWakeLockData
                + ", backgroundWakeLockData="
                + backgroundWakeLockData
                + '}';
        }
    }

    private final List<WakeLock> mWakeLocks;
    private final List<WakeLock> mAggregatedWakeLocks;

    /** @hide **/
    public WakeLockStats(@NonNull List<WakeLock> wakeLocks) {
    /** @hide */
    public WakeLockStats(
            @NonNull List<WakeLock> wakeLocks, @NonNull List<WakeLock> aggregatedWakeLocks) {
        mWakeLocks = wakeLocks;
        mAggregatedWakeLocks = aggregatedWakeLocks;
    }

    @NonNull
@@ -90,22 +179,38 @@ public final class WakeLockStats implements Parcelable {
        return mWakeLocks;
    }

    @NonNull
    public List<WakeLock> getAggregatedWakeLocks() {
        return mAggregatedWakeLocks;
    }

    private WakeLockStats(Parcel in) {
        final int size = in.readInt();
        mWakeLocks = new ArrayList<>(size);
        for (int i = 0; i < size; i++) {
        final int wakelockSize = in.readInt();
        mWakeLocks = new ArrayList<>(wakelockSize);
        for (int i = 0; i < wakelockSize; i++) {
            mWakeLocks.add(new WakeLock(in));
        }
        final int aggregatedWakelockSize = in.readInt();
        mAggregatedWakeLocks = new ArrayList<>(aggregatedWakelockSize);
        for (int i = 0; i < aggregatedWakelockSize; i++) {
            mAggregatedWakeLocks.add(new WakeLock(in));
        }
    }

    @Override
    public void writeToParcel(@NonNull Parcel out, int flags) {
        final int size = mWakeLocks.size();
        out.writeInt(size);
        for (int i = 0; i < size; i++) {
        final int wakelockSize = mWakeLocks.size();
        out.writeInt(wakelockSize);
        for (int i = 0; i < wakelockSize; i++) {
            WakeLock stats = mWakeLocks.get(i);
            stats.writeToParcel(out);
        }
        final int aggregatedWakelockSize = mAggregatedWakeLocks.size();
        out.writeInt(aggregatedWakelockSize);
        for (int i = 0; i < aggregatedWakelockSize; i++) {
            WakeLock stats = mAggregatedWakeLocks.get(i);
            stats.writeToParcel(out);
        }
    }

    @NonNull
@@ -127,6 +232,13 @@ public final class WakeLockStats implements Parcelable {

    @Override
    public String toString() {
        return "WakeLockStats " + mWakeLocks;
        return "WakeLockStats{"
            + "mWakeLocks: ["
            + mWakeLocks
            + "]"
            + ", mAggregatedWakeLocks: ["
            + mAggregatedWakeLocks
            + "]"
            + '}';
    }
}
+131 −14
Original line number Diff line number Diff line
@@ -28,11 +28,115 @@ import java.util.List;
@RunWith(AndroidJUnit4.class)
public class WakeLockStatsTest {

    @Test
    public void isDataValidOfWakeLockData_invalid_returnFalse() {
        WakeLockStats.WakeLockData wakeLockData =
                new WakeLockStats.WakeLockData(
                        /* timesAcquired= */ 0, /* totalTimeHeldMs= */ 10, /* timeHeldMs= */ 0);
        assertThat(wakeLockData.isDataValid()).isFalse();

        wakeLockData =
                new WakeLockStats.WakeLockData(
                        /* timesAcquired= */ 1, /* totalTimeHeldMs= */ 0, /* timeHeldMs= */ 0);
        assertThat(wakeLockData.isDataValid()).isFalse();

        wakeLockData =
                new WakeLockStats.WakeLockData(
                        /* timesAcquired= */ 1, /* totalTimeHeldMs= */ 10, /* timeHeldMs= */ -10);
        assertThat(wakeLockData.isDataValid()).isFalse();

        wakeLockData =
                new WakeLockStats.WakeLockData(
                        /* timesAcquired= */ 1, /* totalTimeHeldMs= */ 10, /* timeHeldMs= */ 20);
        assertThat(wakeLockData.isDataValid()).isFalse();
    }

    @Test
    public void isDataValidOfWakeLockData_empty_returnTrue() {
        final WakeLockStats.WakeLockData wakeLockData = WakeLockStats.WakeLockData.EMPTY;
        assertThat(wakeLockData.isDataValid()).isTrue();
    }

    @Test
    public void isDataValidOfWakeLockData_valid_returnTrue() {
        WakeLockStats.WakeLockData wakeLockData =
                new WakeLockStats.WakeLockData(
                        /* timesAcquired= */ 1, /* totalTimeHeldMs= */ 10, /* timeHeldMs= */ 5);
        assertThat(wakeLockData.isDataValid()).isTrue();
    }

    @Test
    public void isDataValidOfWakeLock_zeroTotalHeldMs_returnFalse() {
        final WakeLockStats.WakeLockData wakeLockData =
                new WakeLockStats.WakeLockData(
                        /* timesAcquired= */ 0, /* totalTimeHeldMs= */ 0, /* timeHeldMs= */ 0);

        assertThat(WakeLockStats.WakeLock.isDataValid(wakeLockData, wakeLockData)).isFalse();
    }

    @Test
    public void isDataValidOfWakeLock_invalidData_returnFalse() {
        final WakeLockStats.WakeLockData totalWakeLockData =
                new WakeLockStats.WakeLockData(
                        /* timesAcquired= */ 6, /* totalTimeHeldMs= */ 60, /* timeHeldMs= */ 20);
        final WakeLockStats.WakeLockData backgroundWakeLockData =
                new WakeLockStats.WakeLockData(
                        /* timesAcquired= */ 0, /* totalTimeHeldMs= */ 10, /* timeHeldMs= */ 0);

        assertThat(WakeLockStats.WakeLock.isDataValid(totalWakeLockData, backgroundWakeLockData))
                .isFalse();
    }

    @Test
    public void isDataValidOfWakeLock_totalSmallerThanBackground_returnFalse() {
        final WakeLockStats.WakeLockData totalWakeLockData =
                new WakeLockStats.WakeLockData(
                        /* timesAcquired= */ 10, /* totalTimeHeldMs= */ 60, /* timeHeldMs= */ 50);
        final WakeLockStats.WakeLockData backgroundWakeLockData =
                new WakeLockStats.WakeLockData(
                        /* timesAcquired= */ 6, /* totalTimeHeldMs= */ 100, /* timeHeldMs= */ 30);

        assertThat(WakeLockStats.WakeLock.isDataValid(totalWakeLockData, backgroundWakeLockData))
                .isFalse();
    }

    @Test
    public void isDataValidOfWakeLock_returnTrue() {
        final WakeLockStats.WakeLockData totalWakeLockData =
                new WakeLockStats.WakeLockData(
                        /* timesAcquired= */ 10, /* totalTimeHeldMs= */ 100, /* timeHeldMs= */ 50);
        final WakeLockStats.WakeLockData backgroundWakeLockData =
                new WakeLockStats.WakeLockData(
                        /* timesAcquired= */ 6, /* totalTimeHeldMs= */ 60, /* timeHeldMs= */ 20);

        assertThat(WakeLockStats.WakeLock.isDataValid(totalWakeLockData, backgroundWakeLockData))
                .isTrue();
    }

    @Test
    public void parcelablity() {
        final WakeLockStats.WakeLockData totalWakeLockData1 =
                new WakeLockStats.WakeLockData(
                        /* timesAcquired= */ 10, /* totalTimeHeldMs= */ 60, /* timeHeldMs= */ 50);
        final WakeLockStats.WakeLockData backgroundWakeLockData1 =
                new WakeLockStats.WakeLockData(
                        /* timesAcquired= */ 6, /* totalTimeHeldMs= */ 100, /* timeHeldMs= */ 30);
        final WakeLockStats.WakeLock wakeLock1 =
                new WakeLockStats.WakeLock(
                        1, "foo", /* isAggregated= */ false, totalWakeLockData1,
                        backgroundWakeLockData1);
        final WakeLockStats.WakeLockData totalWakeLockData2 =
                new WakeLockStats.WakeLockData(
                        /* timesAcquired= */ 20, /* totalTimeHeldMs= */ 80, /* timeHeldMs= */ 30);
        final WakeLockStats.WakeLockData backgroundWakeLockData2 =
                new WakeLockStats.WakeLockData(
                        /* timesAcquired= */ 1, /* totalTimeHeldMs= */ 100, /* timeHeldMs= */ 30);
        final WakeLockStats.WakeLock wakeLock2 =
                new WakeLockStats.WakeLock(
                        2, "bar", /* isAggregated= */ true, totalWakeLockData2,
                        backgroundWakeLockData2);
        WakeLockStats wakeLockStats = new WakeLockStats(
                List.of(new WakeLockStats.WakeLock(1, "foo", 200, 3000, 40000),
                        new WakeLockStats.WakeLock(2, "bar", 500, 6000, 70000)));
                List.of(wakeLock1), List.of(wakeLock2));

        Parcel parcel = Parcel.obtain();
        wakeLockStats.writeToParcel(parcel, 0);
@@ -44,15 +148,28 @@ public class WakeLockStatsTest {
        parcel.setDataPosition(0);

        WakeLockStats actual = WakeLockStats.CREATOR.createFromParcel(parcel);
        assertThat(actual.getWakeLocks()).hasSize(2);
        WakeLockStats.WakeLock wl1 = actual.getWakeLocks().get(0);
        assertThat(wl1.uid).isEqualTo(1);
        assertThat(wl1.name).isEqualTo("foo");
        assertThat(wl1.timesAcquired).isEqualTo(200);
        assertThat(wl1.totalTimeHeldMs).isEqualTo(3000);
        assertThat(wl1.timeHeldMs).isEqualTo(40000);

        WakeLockStats.WakeLock wl2 = actual.getWakeLocks().get(1);
        assertThat(wl2.uid).isEqualTo(2);
        assertThat(actual.getWakeLocks()).hasSize(1);
        WakeLockStats.WakeLock actualWakelock = actual.getWakeLocks().get(0);
        assertThat(actualWakelock.uid).isEqualTo(1);
        assertThat(actualWakelock.name).isEqualTo("foo");
        assertThat(actualWakelock.isAggregated).isFalse();
        assertThat(actualWakelock.totalWakeLockData.timesAcquired).isEqualTo(10);
        assertThat(actualWakelock.totalWakeLockData.totalTimeHeldMs).isEqualTo(60);
        assertThat(actualWakelock.totalWakeLockData.timeHeldMs).isEqualTo(50);
        assertThat(actualWakelock.backgroundWakeLockData.timesAcquired).isEqualTo(6);
        assertThat(actualWakelock.backgroundWakeLockData.totalTimeHeldMs).isEqualTo(100);
        assertThat(actualWakelock.backgroundWakeLockData.timeHeldMs).isEqualTo(30);

        assertThat(actual.getAggregatedWakeLocks()).hasSize(1);
        WakeLockStats.WakeLock actualAggregatedWakelock = actual.getAggregatedWakeLocks().get(0);
        assertThat(actualAggregatedWakelock.uid).isEqualTo(2);
        assertThat(actualAggregatedWakelock.name).isEqualTo("bar");
        assertThat(actualAggregatedWakelock.isAggregated).isTrue();
        assertThat(actualAggregatedWakelock.totalWakeLockData.timesAcquired).isEqualTo(20);
        assertThat(actualAggregatedWakelock.totalWakeLockData.totalTimeHeldMs).isEqualTo(80);
        assertThat(actualAggregatedWakelock.totalWakeLockData.timeHeldMs).isEqualTo(30);
        assertThat(actualAggregatedWakelock.backgroundWakeLockData.timesAcquired).isEqualTo(1);
        assertThat(actualAggregatedWakelock.backgroundWakeLockData.totalTimeHeldMs).isEqualTo(100);
        assertThat(actualAggregatedWakelock.backgroundWakeLockData.timeHeldMs).isEqualTo(30);
    }
}
 No newline at end of file
+59 −15
Original line number Diff line number Diff line
@@ -1591,32 +1591,76 @@ public class BatteryStatsImpl extends BatteryStats {
    @Override
    public WakeLockStats getWakeLockStats() {
        final long realtimeMs = mClock.elapsedRealtime();
        final long realtimeUs = realtimeMs * 1000;
        List<WakeLockStats.WakeLock> uidWakeLockStats = new ArrayList<>();
        List<WakeLockStats.WakeLock> uidAggregatedWakeLockStats = new ArrayList<>();
        for (int i = mUidStats.size() - 1; i >= 0; i--) {
            final Uid uid = mUidStats.valueAt(i);
            // Converts unaggregated wakelocks.
            final ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> wakelockStats =
                    uid.mWakelockStats.getMap();
            for (int j = wakelockStats.size() - 1; j >= 0; j--) {
                final String name = wakelockStats.keyAt(j);
                final Uid.Wakelock wakelock = (Uid.Wakelock) wakelockStats.valueAt(j);
                final DualTimer timer = wakelock.mTimerPartial;
                if (timer != null) {
                    final long totalTimeLockHeldMs =
                            timer.getTotalTimeLocked(realtimeUs, STATS_SINCE_CHARGED) / 1000;
                    if (totalTimeLockHeldMs != 0) {
                        uidWakeLockStats.add(
                                new WakeLockStats.WakeLock(uid.getUid(), name,
                                        timer.getCountLocked(STATS_SINCE_CHARGED),
                                        totalTimeLockHeldMs,
                                        timer.isRunningLocked()
                                                ? timer.getCurrentDurationMsLocked(realtimeMs)
                                                : 0));
                final WakeLockStats.WakeLock wakeLockItem =
                        createWakeLock(uid, name, /* isAggregated= */ false, wakelock.mTimerPartial,
                                realtimeMs);
                if (wakeLockItem != null) {
                    uidWakeLockStats.add(wakeLockItem);
                }
            }
            // Converts aggregated wakelocks.
            final WakeLockStats.WakeLock aggregatedWakeLockItem =
                    createWakeLock(
                    uid,
                    WakeLockStats.WakeLock.NAME_AGGREGATED,
                    /* isAggregated= */ true,
                    uid.mAggregatedPartialWakelockTimer,
                    realtimeMs);
            if (aggregatedWakeLockItem != null) {
                uidAggregatedWakeLockStats.add(aggregatedWakeLockItem);
            }
        }
        return new WakeLockStats(uidWakeLockStats);
        return new WakeLockStats(uidWakeLockStats, uidAggregatedWakeLockStats);
    }
    // Returns a valid {@code WakeLockStats.WakeLock} or null.
    private WakeLockStats.WakeLock createWakeLock(
            Uid uid, String name, boolean isAggregated, DualTimer timer, final long realtimeMs) {
        if (timer == null) {
            return null;
        }
        // Uses the primary timer for total wakelock data and used the sub timer for background
        // wakelock data.
        final WakeLockStats.WakeLockData totalWakeLockData = createWakeLockData(timer, realtimeMs);
        final WakeLockStats.WakeLockData backgroundWakeLockData =
                createWakeLockData(timer.getSubTimer(), realtimeMs);
        return WakeLockStats.WakeLock.isDataValid(totalWakeLockData, backgroundWakeLockData)
                ? new WakeLockStats.WakeLock(
                uid.getUid(),
                name,
                isAggregated,
                totalWakeLockData,
                backgroundWakeLockData) : null;
    }
    @NonNull
    private WakeLockStats.WakeLockData createWakeLockData(
            DurationTimer timer, final long realtimeMs) {
        if (timer == null) {
            return WakeLockStats.WakeLockData.EMPTY;
        }
        final long totalTimeLockHeldMs =
                timer.getTotalTimeLocked(realtimeMs * 1000, STATS_SINCE_CHARGED) / 1000;
        if (totalTimeLockHeldMs == 0) {
            return WakeLockStats.WakeLockData.EMPTY;
        }
        return new WakeLockStats.WakeLockData(
            timer.getCountLocked(STATS_SINCE_CHARGED),
            totalTimeLockHeldMs,
            timer.isRunningLocked() ? timer.getCurrentDurationMsLocked(realtimeMs) : 0);
    }
    @Override
+116 −25

File changed.

Preview size limit exceeded, changes collapsed.