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

Commit b67f4774 authored by Michael Wachenschwanz's avatar Michael Wachenschwanz Committed by Android (Google) Code Review
Browse files

Merge "Switch activity state pushing to ConcurrentLinkedQueue" into main

parents 2a042124 d9dc7dd2
Loading
Loading
Loading
Loading
+54 −18
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ import com.android.server.wm.WindowProcessController;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

@@ -65,6 +66,11 @@ public class ProcessStateController {

    private final GlobalState mGlobalState = new GlobalState();

    /**
     * Queue for staging asynchronous events. The queue will be drained before each update.
     */
    private final ConcurrentLinkedQueue<Runnable> mStagingQueue = new ConcurrentLinkedQueue<>();

    private ProcessStateController(ActivityManagerService ams, ProcessList processList,
            ActiveUids activeUids, ServiceThread handlerThread,
            CachedAppOptimizer cachedAppOptimizer, Object lock, Object procLock,
@@ -115,7 +121,7 @@ public class ProcessStateController {
     */
    @GuardedBy("mLock")
    public boolean runUpdate(@NonNull ProcessRecord proc, @OomAdjReason int oomAdjReason) {
        mGlobalState.commitStagedState();
        commitStagedEvents();
        return mOomAdjuster.updateOomAdjLocked(proc, oomAdjReason);
    }

@@ -124,15 +130,16 @@ public class ProcessStateController {
     */
    @GuardedBy("mLock")
    public void runPendingUpdate(@OomAdjReason int oomAdjReason) {
        mGlobalState.commitStagedState();
        commitStagedEvents();
        mOomAdjuster.updateOomAdjPendingTargetsLocked(oomAdjReason);
    }

    /**
     * Trigger an update on all processes.
     */
    @GuardedBy("mLock")
    public void runFullUpdate(@OomAdjReason int oomAdjReason) {
        mGlobalState.commitStagedState();
        commitStagedEvents();
        mOomAdjuster.updateOomAdjLocked(oomAdjReason);
    }

@@ -140,8 +147,9 @@ public class ProcessStateController {
     * Trigger an update on any processes that have been marked for follow up during a previous
     * update.
     */
    @GuardedBy("mLock")
    public void runFollowUpUpdate() {
        mGlobalState.commitStagedState();
        commitStagedEvents();
        mOomAdjuster.updateOomAdjFollowUpTargetsLocked();
    }

@@ -151,7 +159,7 @@ public class ProcessStateController {
     * @param looper which looper to post the async work to.
     */
    public ActivityStateAsyncUpdater createActivityStateAsyncUpdater(Looper looper) {
        return new ActivityStateAsyncUpdater(this, looper);
        return new ActivityStateAsyncUpdater(this, looper, mStagingQueue);
    }

    /**
@@ -776,6 +784,19 @@ public class ProcessStateController {
        }
    }

    @GuardedBy("mLock")
    private void commitStagedEvents() {
        mGlobalState.commitStagedState();

        if (Flags.pushActivityStateToOomadjuster()) {
            // Drain any activity state changes from the staging queue.
            final ConcurrentLinkedQueue<Runnable> queue = mStagingQueue;
            while (!queue.isEmpty()) {
                queue.poll().run();
            }
        }
    }

    /**
     * Helper class for sending Activity related state from Window Manager to
     * ProcessStateController. Because ProcessStateController is guarded by a lock WindowManager
@@ -788,11 +809,14 @@ public class ProcessStateController {
    public static class ActivityStateAsyncUpdater {
        private final ProcessStateController mPsc;
        private final Looper mLooper;
        private ConcurrentLinkedQueue<Runnable> mStagingQueue;
        private AsyncBatchSession mBatchSession;

        private ActivityStateAsyncUpdater(ProcessStateController psc, Looper looper) {
        private ActivityStateAsyncUpdater(ProcessStateController psc, Looper looper,
                ConcurrentLinkedQueue<Runnable> stagingQueue) {
            mPsc = psc;
            mLooper = looper;
            mStagingQueue = stagingQueue;
        }

        /**
@@ -829,7 +853,7 @@ public class ProcessStateController {
        public void setExpandedNotificationShadeAsync(boolean expandedShade) {
            if (!Flags.pushActivityStateToOomadjuster()) return;

            getBatchSession().enqueue(() -> mPsc.setExpandedNotificationShade(expandedShade));
            getBatchSession().stage(() -> mPsc.setExpandedNotificationShade(expandedShade));
        }

        /**
@@ -840,7 +864,7 @@ public class ProcessStateController {
            if (!Flags.pushActivityStateToOomadjuster()) return;

            final ProcessRecord top = wpc != null ? (ProcessRecord) wpc.mOwner : null;
            getBatchSession().enqueue(() -> {
            getBatchSession().stage(() -> {
                mPsc.setTopProcess(top);
                if (clearPrev) {
                    mPsc.setPreviousProcess(null);
@@ -857,7 +881,7 @@ public class ProcessStateController {
        public void setTopProcessStateAsync(@ActivityManager.ProcessState int procState) {
            if (!Flags.pushActivityStateToOomadjuster()) return;

            getBatchSession().enqueue(() -> mPsc.setTopProcessState(procState));
            getBatchSession().stage(() -> mPsc.setTopProcessState(procState));
        }

        /**
@@ -867,7 +891,7 @@ public class ProcessStateController {
            if (!Flags.pushActivityStateToOomadjuster()) return;

            final ProcessRecord prev = wpc != null ? (ProcessRecord) wpc.mOwner : null;
            getBatchSession().enqueue(() -> mPsc.setPreviousProcess(prev));
            getBatchSession().stage(() -> mPsc.setPreviousProcess(prev));
        }


@@ -878,7 +902,7 @@ public class ProcessStateController {
            if (!Flags.pushActivityStateToOomadjuster()) return;

            final ProcessRecord home = wpc != null ? (ProcessRecord) wpc.mOwner : null;
            getBatchSession().enqueue(() -> mPsc.setHomeProcess(home));
            getBatchSession().stage(() -> mPsc.setHomeProcess(home));
        }


@@ -889,7 +913,7 @@ public class ProcessStateController {
            if (!Flags.pushActivityStateToOomadjuster()) return;

            final ProcessRecord heavy = wpc != null ? (ProcessRecord) wpc.mOwner : null;
            getBatchSession().enqueue(() -> mPsc.setHeavyWeightProcess(heavy));
            getBatchSession().stage(() -> mPsc.setHeavyWeightProcess(heavy));
        }

        /**
@@ -899,7 +923,7 @@ public class ProcessStateController {
            if (!Flags.pushActivityStateToOomadjuster()) return;

            final ProcessRecord dozeUi = wpc != null ? (ProcessRecord) wpc.mOwner : null;
            getBatchSession().enqueue(() -> mPsc.setVisibleDozeUiProcess(dozeUi));
            getBatchSession().stage(() -> mPsc.setVisibleDozeUiProcess(dozeUi));
        }

        /**
@@ -909,7 +933,7 @@ public class ProcessStateController {
            if (!Flags.pushActivityStateToOomadjuster()) return;

            final ProcessRecordInternal activity = (ProcessRecordInternal) wpc.mOwner;
            getBatchSession().enqueue(() -> mPsc.setHasActivity(activity, hasActivity));
            getBatchSession().stage(() -> mPsc.setHasActivity(activity, hasActivity));
        }

        /**
@@ -920,7 +944,7 @@ public class ProcessStateController {
            if (!Flags.pushActivityStateToOomadjuster()) return;

            final ProcessRecordInternal activity = (ProcessRecordInternal) wpc.mOwner;
            getBatchSession().enqueue(() -> {
            getBatchSession().stage(() -> {
                mPsc.setActivityStateFlags(activity, flags);
                mPsc.setPerceptibleTaskStoppedTimeMillis(activity, perceptibleStopTimeMs);
            });
@@ -934,14 +958,14 @@ public class ProcessStateController {
            if (!Flags.pushActivityStateToOomadjuster()) return;

            final ProcessRecordInternal proc = (ProcessRecordInternal) wpc.mOwner;
            getBatchSession().enqueue(() -> mPsc.setHasRecentTasks(proc, hasRecentTasks));
            getBatchSession().stage(() -> mPsc.setHasRecentTasks(proc, hasRecentTasks));
        }

        private AsyncBatchSession getBatchSession() {
            if (mBatchSession == null) {
                final Handler h = new Handler(mLooper);
                final Runnable update = () -> mPsc.runFullUpdate(OOM_ADJ_REASON_ACTIVITY);
                mBatchSession = new AsyncBatchSession(h, mPsc.mLock, update);
                mBatchSession = new AsyncBatchSession(h, mPsc.mLock, mStagingQueue, update);
            }
            return mBatchSession;
        }
@@ -950,6 +974,7 @@ public class ProcessStateController {
    public static class AsyncBatchSession implements AutoCloseable {
        final Handler mHandler;
        final Object mLock;
        final ConcurrentLinkedQueue<Runnable> mStagingQueue;
        private final Runnable mUpdateRunnable;
        private final Runnable mLockedUpdateRunnable;
        private boolean mRunUpdate = false;
@@ -958,9 +983,11 @@ public class ProcessStateController {

        private ArrayList<Runnable> mBatchList = new ArrayList<>();

        AsyncBatchSession(Handler handler, Object lock, Runnable updateRunnable) {
        AsyncBatchSession(Handler handler, Object lock,
                ConcurrentLinkedQueue<Runnable> stagingQueue, Runnable updateRunnable) {
            mHandler = handler;
            mLock = lock;
            mStagingQueue = stagingQueue;
            mUpdateRunnable = updateRunnable;
            mLockedUpdateRunnable = () -> {
                synchronized (lock) {
@@ -979,6 +1006,15 @@ public class ProcessStateController {
            }
        }

        /**
         * Stage the runnable to be run on the next ProcessStateController update. The work may be
         * opportunistically run if an update triggers before the WindowManager posted update is
         * handled.
         */
        public void stage(Runnable runnable) {
            mStagingQueue.add(runnable);
        }

        /**
         * Enqueue the work to be run asynchronously done on a Handler thread.
         * If batch session is currently active, queue up the work to be run when the session ends.
+72 −4
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ import org.junit.Before;
import org.junit.Test;

import java.util.ArrayList;
import java.util.concurrent.ConcurrentLinkedQueue;

@Presubmit
public class ProcessStateControllerTest {
@@ -91,7 +92,7 @@ public class ProcessStateControllerTest {
    public void asyncBatchSession_enqueue() {
        ArrayList<String> list = new ArrayList<>();
        ProcessStateController.AsyncBatchSession session =
                new ProcessStateController.AsyncBatchSession(mManagedHandler, new Object(),
                new ProcessStateController.AsyncBatchSession(mManagedHandler, new Object(), null,
                        () -> list.add("UPDATED"));

        // Enqueue some work and trigger an update mid way, while batching is active.
@@ -118,7 +119,7 @@ public class ProcessStateControllerTest {
    public void asyncBatchSession_enqueue_batched() {
        ArrayList<String> list = new ArrayList<>();
        ProcessStateController.AsyncBatchSession session =
                new ProcessStateController.AsyncBatchSession(mManagedHandler, new Object(),
                new ProcessStateController.AsyncBatchSession(mManagedHandler, new Object(), null,
                        () -> list.add("UPDATED"));

        // Enqueue some work and trigger an update mid way, while batching is active.
@@ -144,7 +145,7 @@ public class ProcessStateControllerTest {
    public void asyncBatchSession_enqueueNoUpdate_batched() {
        ArrayList<String> list = new ArrayList<>();
        ProcessStateController.AsyncBatchSession session =
                new ProcessStateController.AsyncBatchSession(mManagedHandler, new Object(),
                new ProcessStateController.AsyncBatchSession(mManagedHandler, new Object(), null,
                        () -> list.add("UPDATED"));

        // Enqueue some work and trigger an update mid way, while batching is active.
@@ -166,7 +167,7 @@ public class ProcessStateControllerTest {
    public void asyncBatchSession_enqueueBoostPriority_batched() {
        ArrayList<String> list = new ArrayList<>();
        ProcessStateController.AsyncBatchSession session =
                new ProcessStateController.AsyncBatchSession(mManagedHandler, new Object(),
                new ProcessStateController.AsyncBatchSession(mManagedHandler, new Object(), null,
                        () -> list.add("UPDATED"));

        // Enqueue some work , while batching is active and boost the priority of the session.
@@ -185,4 +186,71 @@ public class ProcessStateControllerTest {
        mTestLooperManager.execute(mTestLooperManager.next());
        assertThat(list).containsExactly("A", "B", "X");
    }

    @Test
    public void asyncBatchSession_interlacedEnqueueAndStage() {
        ArrayList<String> list = new ArrayList<>();
        ConcurrentLinkedQueue<Runnable> stagingQueue = new ConcurrentLinkedQueue<>();
        ProcessStateController.AsyncBatchSession session =
                new ProcessStateController.AsyncBatchSession(mManagedHandler, new Object(),
                        stagingQueue, () -> list.add("UPDATED"));

        // Enqueue some work and trigger an update mid way, while batching is active.
        session.stage(() -> list.add("1"));
        session.enqueue(() -> list.add("A"));
        session.runUpdate();
        session.stage(() -> list.add("2"));

        // Run the first staged runnable.
        stagingQueue.poll().run();
        assertThat(list).containsExactly("1");
        // Step through the looper one
        mTestLooperManager.execute(mTestLooperManager.next());
        assertThat(list).containsExactly("1", "A");
        // Run the second staged runnable.
        stagingQueue.poll().run();
        assertThat(list).containsExactly("1", "A", "2");
        // Step through the looper once more.
        mTestLooperManager.execute(mTestLooperManager.next());
        assertThat(list).containsExactly("1", "A", "2", "UPDATED");
    }

    @Test
    public void asyncBatchSession_interlacedEnqueueAndStage_batched() {
        ArrayList<String> list = new ArrayList<>();
        ConcurrentLinkedQueue<Runnable> stagingQueue = new ConcurrentLinkedQueue<>();
        ProcessStateController.AsyncBatchSession session =
                new ProcessStateController.AsyncBatchSession(mManagedHandler, new Object(),
                        stagingQueue, () -> list.add("UPDATED"));

        // Enqueue some work and trigger an update mid way, while batching is active.
        session.start();
        session.stage(() -> list.add("1"));
        session.enqueue(() -> list.add("A"));
        session.stage(() -> list.add("2"));
        session.runUpdate();
        session.enqueue(() -> list.add("B"));
        session.stage(() -> list.add("3"));
        session.stage(() -> list.add("4"));
        session.close();

        // Run the first staged runnable.
        stagingQueue.poll().run();
        // Run the second staged runnable.
        stagingQueue.poll().run();
        // Run the third staged runnable.
        stagingQueue.poll().run();
        // Step through the looper once to run all batched enqueued work.
        mTestLooperManager.execute(mTestLooperManager.next());
        // Run the last staged runnable.
        stagingQueue.poll().run();

        assertThat(list.get(0)).isEqualTo("1");
        assertThat(list.get(1)).isEqualTo("2");
        assertThat(list.get(2)).isEqualTo("3");
        assertThat(list.get(3)).isEqualTo("A");
        assertThat(list.get(4)).isEqualTo("B");
        assertThat(list.get(5)).isEqualTo("UPDATED");
        assertThat(list.get(6)).isEqualTo("4");
    }
}