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

Commit 0b2d4b49 authored by Dmitri Plotnikov's avatar Dmitri Plotnikov
Browse files

Preserve active kernel wakelocks across batterystats reset

Bug: 278146742
Test: atest FrameworksServicesTests

Change-Id: Ib3a0d53a17065ed303a9133f84cbd3b12a6de1b4
parent e47a4338
Loading
Loading
Loading
Loading
+40 −24
Original line number Diff line number Diff line
@@ -251,9 +251,10 @@ public class BatteryStatsImpl extends BatteryStats {
    private static final LongCounter[] ZERO_LONG_COUNTER_ARRAY =
            new LongCounter[]{ZERO_LONG_COUNTER};
    private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader();
    private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
    @VisibleForTesting
    protected KernelWakelockReader mKernelWakelockReader;
    @VisibleForTesting
    protected KernelCpuUidUserSysTimeReader mCpuUidUserSysTimeReader;
    @VisibleForTesting
@@ -1763,6 +1764,7 @@ public class BatteryStatsImpl extends BatteryStats {
        mCpuUidFreqTimeReader = new KernelCpuUidFreqTimeReader(true, clock);
        mCpuUidActiveTimeReader = new KernelCpuUidActiveTimeReader(true, clock);
        mCpuUidClusterTimeReader = new KernelCpuUidClusterTimeReader(true, clock);
        mKernelWakelockReader = new KernelWakelockReader();
    }
    /**
@@ -2675,19 +2677,18 @@ public class BatteryStatsImpl extends BatteryStats {
         * The reported count from /proc/wakelocks when unplug() was last
         * called.
         */
        int mUnpluggedReportedCount;
        int mBaseReportedCount;
        /**
         * The most recent reported total_time from /proc/wakelocks.
         */
        long mCurrentReportedTotalTimeUs;
        /**
         * The reported total_time from /proc/wakelocks when unplug() was last
         * called.
         */
        long mUnpluggedReportedTotalTimeUs;
        long mBaseReportedTotalTimeUs;
        /**
         * Whether we are currently in a discharge cycle.
@@ -2708,9 +2709,9 @@ public class BatteryStatsImpl extends BatteryStats {
        public SamplingTimer(Clock clock, TimeBase timeBase, Parcel in) {
            super(clock, 0, timeBase, in);
            mCurrentReportedCount = in.readInt();
            mUnpluggedReportedCount = in.readInt();
            mBaseReportedCount = in.readInt();
            mCurrentReportedTotalTimeUs = in.readLong();
            mUnpluggedReportedTotalTimeUs = in.readLong();
            mBaseReportedTotalTimeUs = in.readLong();
            mTrackingReportedValues = in.readInt() == 1;
            mTimeBaseRunning = timeBase.isRunning();
        }
@@ -2736,8 +2737,8 @@ public class BatteryStatsImpl extends BatteryStats {
        public void endSample(long elapsedRealtimeUs) {
            mTotalTimeUs = computeRunTimeLocked(0 /* unused by us */, elapsedRealtimeUs);
            mCount = computeCurrentCountLocked();
            mUnpluggedReportedTotalTimeUs = mCurrentReportedTotalTimeUs = 0;
            mUnpluggedReportedCount = mCurrentReportedCount = 0;
            mBaseReportedTotalTimeUs = mCurrentReportedTotalTimeUs = 0;
            mBaseReportedCount = mCurrentReportedCount = 0;
            mTrackingReportedValues = false;
        }
@@ -2762,10 +2763,21 @@ public class BatteryStatsImpl extends BatteryStats {
         * @param count total number of times the event being sampled occurred.
         */
        public void update(long totalTimeUs, int count, long elapsedRealtimeUs) {
            update(totalTimeUs, 0, count, elapsedRealtimeUs);
        }
        /**
         * Updates the current recorded values. See {@link #update(long, int, long)}
         *
         * @param activeTimeUs Time that the currently active wake lock has been held.
         */
        public void update(long totalTimeUs, long activeTimeUs, int count,
                long elapsedRealtimeUs) {
            if (mTimeBaseRunning && !mTrackingReportedValues) {
                // Updating the reported value for the first time.
                mUnpluggedReportedTotalTimeUs = totalTimeUs;
                mUnpluggedReportedCount = count;
                // Updating the reported value for the first time. If the wake lock is currently
                // active, mark the time it was acquired as the base timestamp.
                mBaseReportedTotalTimeUs = totalTimeUs - activeTimeUs;
                mBaseReportedCount = activeTimeUs == 0 ? count : count - 1;
            }
            mTrackingReportedValues = true;
@@ -2800,8 +2812,8 @@ public class BatteryStatsImpl extends BatteryStats {
        public void onTimeStarted(long elapsedRealtimeUs, long baseUptimeUs, long baseRealtimeUs) {
            super.onTimeStarted(elapsedRealtimeUs, baseUptimeUs, baseRealtimeUs);
            if (mTrackingReportedValues) {
                mUnpluggedReportedTotalTimeUs = mCurrentReportedTotalTimeUs;
                mUnpluggedReportedCount = mCurrentReportedCount;
                mBaseReportedTotalTimeUs = mCurrentReportedTotalTimeUs;
                mBaseReportedCount = mCurrentReportedCount;
            }
            mTimeBaseRunning = true;
        }
@@ -2816,30 +2828,30 @@ public class BatteryStatsImpl extends BatteryStats {
        public void logState(Printer pw, String prefix) {
            super.logState(pw, prefix);
            pw.println(prefix + "mCurrentReportedCount=" + mCurrentReportedCount
                    + " mUnpluggedReportedCount=" + mUnpluggedReportedCount
                    + " mBaseReportedCount=" + mBaseReportedCount
                    + " mCurrentReportedTotalTime=" + mCurrentReportedTotalTimeUs
                    + " mUnpluggedReportedTotalTime=" + mUnpluggedReportedTotalTimeUs);
                    + " mBaseReportedTotalTimeUs=" + mBaseReportedTotalTimeUs);
        }
        @Override
        protected long computeRunTimeLocked(long curBatteryRealtime, long elapsedRealtimeUs) {
            return mTotalTimeUs + (mTimeBaseRunning && mTrackingReportedValues
                    ? mCurrentReportedTotalTimeUs - mUnpluggedReportedTotalTimeUs : 0);
                    ? mCurrentReportedTotalTimeUs - mBaseReportedTotalTimeUs : 0);
        }
        @Override
        protected int computeCurrentCountLocked() {
            return mCount + (mTimeBaseRunning && mTrackingReportedValues
                    ? mCurrentReportedCount - mUnpluggedReportedCount : 0);
                    ? mCurrentReportedCount - mBaseReportedCount : 0);
        }
        @Override
        public void writeToParcel(Parcel out, long elapsedRealtimeUs) {
            super.writeToParcel(out, elapsedRealtimeUs);
            out.writeInt(mCurrentReportedCount);
            out.writeInt(mUnpluggedReportedCount);
            out.writeInt(mBaseReportedCount);
            out.writeLong(mCurrentReportedTotalTimeUs);
            out.writeLong(mUnpluggedReportedTotalTimeUs);
            out.writeLong(mBaseReportedTotalTimeUs);
            out.writeInt(mTrackingReportedValues ? 1 : 0);
        }
@@ -2847,8 +2859,8 @@ public class BatteryStatsImpl extends BatteryStats {
        public boolean reset(boolean detachIfReset, long elapsedRealtimeUs) {
            super.reset(detachIfReset, elapsedRealtimeUs);
            mTrackingReportedValues = false;
            mUnpluggedReportedTotalTimeUs = 0;
            mUnpluggedReportedCount = 0;
            mBaseReportedTotalTimeUs = 0;
            mBaseReportedCount = 0;
            return true;
        }
    }
@@ -13398,6 +13410,10 @@ public class BatteryStatsImpl extends BatteryStats {
     * Read and distribute kernel wake lock use across apps.
     */
    public void updateKernelWakelocksLocked(long elapsedRealtimeUs) {
        if (mKernelWakelockReader == null) {
            return;
        }
        final KernelWakelockStats wakelockStats = mKernelWakelockReader.readKernelWakelockStats(
                mTmpWakelockStats);
        if (wakelockStats == null) {
@@ -13416,8 +13432,8 @@ public class BatteryStatsImpl extends BatteryStats {
                mKernelWakelockStats.put(name, kwlt);
            }
            kwlt.update(kws.mTotalTime, kws.mCount, elapsedRealtimeUs);
            kwlt.setUpdateVersion(kws.mVersion);
            kwlt.update(kws.totalTimeUs, kws.activeTimeUs, kws.count, elapsedRealtimeUs);
            kwlt.setUpdateVersion(kws.version);
        }
        int numWakelocksSetStale = 0;
@@ -13471,7 +13487,7 @@ public class BatteryStatsImpl extends BatteryStats {
                Slog.d(TAG, String.format("Added entry %d and updated timer to: "
                        + "mUnpluggedReportedTotalTimeUs %d size %d", bandwidthEntries.keyAt(i),
                        mKernelMemoryStats.get(
                                bandwidthEntries.keyAt(i)).mUnpluggedReportedTotalTimeUs,
                                bandwidthEntries.keyAt(i)).mBaseReportedTotalTimeUs,
                        mKernelMemoryStats.size()));
            }
        }
+53 −42
Original line number Diff line number Diff line
@@ -43,29 +43,30 @@ public class KernelWakelockReader {
    private static final String sSysClassWakeupDir = "/sys/class/wakeup";

    private static final int[] PROC_WAKELOCKS_FORMAT = new int[]{
        Process.PROC_TAB_TERM|Process.PROC_OUT_STRING|                // 0: name
                              Process.PROC_QUOTES,
            Process.PROC_TAB_TERM | Process.PROC_OUT_STRING                 // 0: name
                    | Process.PROC_QUOTES,
            Process.PROC_TAB_TERM | Process.PROC_OUT_LONG,                  // 1: count
            Process.PROC_TAB_TERM,
        Process.PROC_TAB_TERM,
            Process.PROC_TAB_TERM | Process.PROC_OUT_LONG,                  // 3: activeSince
            Process.PROC_TAB_TERM,
            Process.PROC_TAB_TERM | Process.PROC_OUT_LONG,                  // 5: totalTime
    };

    private static final int[] WAKEUP_SOURCES_FORMAT = new int[]{
            Process.PROC_TAB_TERM | Process.PROC_OUT_STRING,                // 0: name
        Process.PROC_TAB_TERM|Process.PROC_COMBINE|
                              Process.PROC_OUT_LONG,                  // 1: count
        Process.PROC_TAB_TERM|Process.PROC_COMBINE,
            Process.PROC_TAB_TERM | Process.PROC_COMBINE
                    | Process.PROC_OUT_LONG,                                // 1: count
            Process.PROC_TAB_TERM | Process.PROC_COMBINE,
            Process.PROC_TAB_TERM | Process.PROC_COMBINE,
            Process.PROC_TAB_TERM | Process.PROC_COMBINE,
            Process.PROC_TAB_TERM | Process.PROC_COMBINE
                    | Process.PROC_OUT_LONG,                                // 5: activeSince
            Process.PROC_TAB_TERM | Process.PROC_COMBINE
                    | Process.PROC_OUT_LONG,                                // 6: totalTime
    };

    private final String[] mProcWakelocksName = new String[3];
    private final long[] mProcWakelocksData = new long[3];
    private final long[] mProcWakelocksData = new long[4];
    private ISuspendControlServiceInternal mSuspendControlService = null;
    private byte[] mKernelWakelockBuffer = new byte[32 * 1024];

@@ -74,7 +75,7 @@ public class KernelWakelockReader {
     * @param staleStats Existing object to update.
     * @return the updated data.
     */
    public final KernelWakelockStats readKernelWakelockStats(KernelWakelockStats staleStats) {
    public KernelWakelockStats readKernelWakelockStats(KernelWakelockStats staleStats) {
        boolean useSystemSuspend = (new File(sSysClassWakeupDir)).exists();

        if (useSystemSuspend) {
@@ -180,14 +181,16 @@ public class KernelWakelockReader {
     */
    private KernelWakelockStats getWakelockStatsFromSystemSuspend(
            final KernelWakelockStats staleStats) {
        WakeLockInfo[] wlStats = null;
        if (mSuspendControlService == null) {
            try {
                mSuspendControlService = waitForSuspendControlService();
            } catch (ServiceNotFoundException e) {
                Slog.wtf(TAG, "Required service suspend_control not available", e);
                return null;
            }
        }

        WakeLockInfo[] wlStats;
        try {
            wlStats = mSuspendControlService.getWakeLockStats();
            updateWakelockStats(wlStats, staleStats);
@@ -210,13 +213,16 @@ public class KernelWakelockReader {
        for (WakeLockInfo info : wlStats) {
            if (!staleStats.containsKey(info.name)) {
                staleStats.put(info.name, new KernelWakelockStats.Entry((int) info.activeCount,
                        info.totalTime * 1000 /* ms to us */, sKernelWakelockUpdateVersion));
                        info.totalTime * 1000 /* ms to us */,
                        info.isActive ? info.activeTime * 1000 : 0,
                        sKernelWakelockUpdateVersion));
            } else {
                KernelWakelockStats.Entry kwlStats = staleStats.get(info.name);
                kwlStats.mCount = (int) info.activeCount;
                kwlStats.count = (int) info.activeCount;
                // Convert milliseconds to microseconds
                kwlStats.mTotalTime = info.totalTime * 1000;
                kwlStats.mVersion = sKernelWakelockUpdateVersion;
                kwlStats.totalTimeUs = info.totalTime * 1000;
                kwlStats.activeTimeUs = info.isActive ? info.activeTime * 1000 : 0;
                kwlStats.version = sKernelWakelockUpdateVersion;
            }
        }

@@ -232,6 +238,7 @@ public class KernelWakelockReader {
        String name;
        int count;
        long totalTime;
        long activeTime;
        int startIndex;
        int endIndex;

@@ -269,25 +276,29 @@ public class KernelWakelockReader {

                if (wakeup_sources) {
                    // convert milliseconds to microseconds
                        totalTime = wlData[2] * 1000;
                    activeTime = wlData[2] * 1000;
                    totalTime = wlData[3] * 1000;
                } else {
                    // convert nanoseconds to microseconds with rounding.
                        totalTime = (wlData[2] + 500) / 1000;
                    activeTime = (wlData[2] + 500) / 1000;
                    totalTime = (wlData[3] + 500) / 1000;
                }

                if (parsed && name.length() > 0) {
                    if (!staleStats.containsKey(name)) {
                        staleStats.put(name, new KernelWakelockStats.Entry(count, totalTime,
                                sKernelWakelockUpdateVersion));
                                activeTime, sKernelWakelockUpdateVersion));
                    } else {
                        KernelWakelockStats.Entry kwlStats = staleStats.get(name);
                        if (kwlStats.mVersion == sKernelWakelockUpdateVersion) {
                            kwlStats.mCount += count;
                            kwlStats.mTotalTime += totalTime;
                        if (kwlStats.version == sKernelWakelockUpdateVersion) {
                            kwlStats.count += count;
                            kwlStats.totalTimeUs += totalTime;
                            kwlStats.activeTimeUs = activeTime;
                        } else {
                            kwlStats.mCount = count;
                            kwlStats.mTotalTime = totalTime;
                            kwlStats.mVersion = sKernelWakelockUpdateVersion;
                            kwlStats.count = count;
                            kwlStats.totalTimeUs = totalTime;
                            kwlStats.activeTimeUs = activeTime;
                            kwlStats.version = sKernelWakelockUpdateVersion;
                        }
                    }
                } else if (!parsed) {
@@ -326,7 +337,7 @@ public class KernelWakelockReader {
    public KernelWakelockStats removeOldStats(final KernelWakelockStats staleStats) {
        Iterator<KernelWakelockStats.Entry> itr = staleStats.values().iterator();
        while (itr.hasNext()) {
            if (itr.next().mVersion != sKernelWakelockUpdateVersion) {
            if (itr.next().version != sKernelWakelockUpdateVersion) {
                itr.remove();
            }
        }
+9 −7
Original line number Diff line number Diff line
@@ -22,14 +22,16 @@ import java.util.HashMap;
 */
public class KernelWakelockStats extends HashMap<String, KernelWakelockStats.Entry> {
    public static class Entry {
        public int mCount;
        public long mTotalTime;
        public int mVersion;
        public int count;
        public long totalTimeUs;
        public long activeTimeUs;
        public int version;

        Entry(int count, long totalTime, int version) {
            mCount = count;
            mTotalTime = totalTime;
            mVersion = version;
        Entry(int count, long totalTimeUs, long activeTimeUs, int version) {
            this.count = count;
            this.totalTimeUs = totalTimeUs;
            this.activeTimeUs = activeTimeUs;
            this.version = version;
        }
    }

+1 −1
Original line number Diff line number Diff line
@@ -1733,7 +1733,7 @@ public class StatsPullAtomService extends SystemService {
            String name = ent.getKey();
            KernelWakelockStats.Entry kws = ent.getValue();
            pulledData.add(FrameworkStatsLog.buildStatsEvent(
                    atomTag, name, kws.mCount, kws.mVersion, kws.mTotalTime));
                    atomTag, name, kws.count, kws.version, kws.totalTimeUs));
        }
        return StatsManager.PULL_SUCCESS;
    }
+51 −1
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import static android.os.BatteryStats.Uid.PROCESS_STATE_FOREGROUND_SERVICE;
import static android.os.BatteryStats.Uid.PROCESS_STATE_TOP;

import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;

import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -55,6 +56,7 @@ import com.android.internal.os.LongArrayMultiStateCounter;
import com.android.internal.os.PowerProfile;

import com.google.common.collect.ImmutableList;
import com.google.common.truth.LongSubject;

import org.junit.Before;
import org.junit.Test;
@@ -77,6 +79,9 @@ public class BatteryStatsImplTest {
    private KernelSingleUidTimeReader mKernelSingleUidTimeReader;
    @Mock
    private PowerProfile mPowerProfile;
    @Mock
    private KernelWakelockReader mKernelWakelockReader;
    private KernelWakelockStats mKernelWakelockStats = new KernelWakelockStats();

    private final MockClock mMockClock = new MockClock();
    private MockBatteryStatsImpl mBatteryStatsImpl;
@@ -89,10 +94,13 @@ public class BatteryStatsImplTest {
        when(mKernelUidCpuFreqTimeReader.readFreqs(any())).thenReturn(CPU_FREQS);
        when(mKernelUidCpuFreqTimeReader.allUidTimesAvailable()).thenReturn(true);
        when(mKernelSingleUidTimeReader.singleUidCpuTimesAvailable()).thenReturn(true);
        when(mKernelWakelockReader.readKernelWakelockStats(
                any(KernelWakelockStats.class))).thenReturn(mKernelWakelockStats);
        mBatteryStatsImpl = new MockBatteryStatsImpl(mMockClock)
                .setPowerProfile(mPowerProfile)
                .setKernelCpuUidFreqTimeReader(mKernelUidCpuFreqTimeReader)
                .setKernelSingleUidTimeReader(mKernelSingleUidTimeReader);
                .setKernelSingleUidTimeReader(mKernelSingleUidTimeReader)
                .setKernelWakelockReader(mKernelWakelockReader);
    }

    @Test
@@ -558,6 +566,48 @@ public class BatteryStatsImplTest {
        assertThat(wakeLock2.totalTimeHeldMs).isEqualTo(4000); // (5000-4000) + (9000-6000)
    }

    @Test
    public void kernelWakelocks() {
        mBatteryStatsImpl.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);

        mKernelWakelockStats.put("lock1", new KernelWakelockStats.Entry(42, 1000, 314, 0));
        mKernelWakelockStats.put("lock2", new KernelWakelockStats.Entry(6, 2000, 0, 0));

        mMockClock.realtime = 5000;

        // The fist call makes a snapshot of the initial state of the wakelocks
        mBatteryStatsImpl.updateKernelWakelocksLocked(mMockClock.realtime * 1000);

        assertThat(mBatteryStatsImpl.getKernelWakelockStats()).hasSize(2);

        mMockClock.realtime += 2000;

        assertThatKernelWakelockTotalTime("lock1").isEqualTo(314);  // active
        assertThatKernelWakelockTotalTime("lock2").isEqualTo(0);        // inactive

        mKernelWakelockStats.put("lock1", new KernelWakelockStats.Entry(43, 1100, 414, 0));
        mKernelWakelockStats.put("lock2", new KernelWakelockStats.Entry(6, 2222, 0, 0));

        mMockClock.realtime += 3000;

        // Compute delta from the initial snapshot
        mBatteryStatsImpl.updateKernelWakelocksLocked(mMockClock.realtime * 1000);

        mMockClock.realtime += 4000;

        assertThatKernelWakelockTotalTime("lock1").isEqualTo(414);

        // Wake lock not active. Expect relative total time as reported by Kernel:
        // 2_222 - 2_000 = 222
        assertThatKernelWakelockTotalTime("lock2").isEqualTo(222);
    }

    private LongSubject assertThatKernelWakelockTotalTime(String name) {
        return assertWithMessage("Kernel wakelock " + name + " at " + mMockClock.realtime)
                .that(mBatteryStatsImpl.getKernelWakelockStats().get(name)
                        .getTotalTimeLocked(mMockClock.realtime * 1000, 0));
    }

    @Test
    public void testGetBluetoothBatteryStats() {
        when(mPowerProfile.getAveragePower(
Loading