Merge "Show install type label for instant app"
diff --git a/Android.bp b/Android.bp
index 2c550fd..2f5c851 100644
--- a/Android.bp
+++ b/Android.bp
@@ -342,6 +342,14 @@
         "staledataclass-annotation-processor",
         "error_prone_android_framework",
     ],
+    // Exports needed for staledataclass-annotation-processor, see b/139342589.
+    javacflags: [
+        "-J--add-modules=jdk.compiler",
+        "-J--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
+        "-J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED",
+        "-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
+        "-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
+    ],
     required: [
         // TODO(b/120066492): remove default_television.xml when the build system
         // propagates "required" properly.
diff --git a/apct-tests/perftests/core/src/android/opengl/perftests/MatrixPerfTest.java b/apct-tests/perftests/core/src/android/opengl/perftests/MatrixPerfTest.java
new file mode 100644
index 0000000..b296148
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/opengl/perftests/MatrixPerfTest.java
@@ -0,0 +1,397 @@
+/*
+ * Copyright (C) 2022 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 android.opengl.perftests;
+
+import android.opengl.Matrix;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.filters.LargeTest;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.Random;
+
+@LargeTest
+public class MatrixPerfTest {
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    @Rule
+    public float[] array = new float[48];
+
+    @Rule
+    public float[] bigArray = new float[16 * 1024 * 1024];
+
+
+    @Test
+    public void testMultiplyMM() {
+        Random rng = new Random();
+        for (int i = 0; i < 32; i++) {
+            array[i] = rng.nextFloat();
+        }
+
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            Matrix.multiplyMM(array, 32, array, 16, array, 0);
+        }
+    }
+
+    @Test
+    public void testMultiplyMMLeftOverlapResult() {
+        Random rng = new Random();
+        for (int i = 0; i < 32; i++) {
+            array[i] = rng.nextFloat();
+        }
+
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            Matrix.multiplyMM(array, 16, array, 16, array, 0);
+        }
+    }
+
+    @Test
+    public void testMultiplyMMRightOverlapResult() {
+        Random rng = new Random();
+        for (int i = 0; i < 32; i++) {
+            array[i] = rng.nextFloat();
+        }
+
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            Matrix.multiplyMM(array, 0, array, 16, array, 0);
+        }
+    }
+
+    @Test
+    public void testMultiplyMMAllOverlap() {
+        Random rng = new Random();
+        for (int i = 0; i < 16; i++) {
+            array[i] = rng.nextFloat();
+        }
+
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            Matrix.multiplyMM(array, 0, array, 0, array, 0);
+        }
+    }
+
+    @Test
+    public void testMultiplyMMOutputBigArray() {
+        Random rng = new Random();
+        for (int i = 0; i < 32; i++) {
+            array[i] = rng.nextFloat();
+        }
+
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            Matrix.multiplyMM(bigArray, 1024, array, 16, array, 0);
+        }
+    }
+
+    @Test
+    public void testMultiplyMMAllBigArray() {
+        Random rng = new Random();
+        for (int i = 0; i < 32; i++) {
+            bigArray[i] = rng.nextFloat();
+        }
+
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            Matrix.multiplyMM(bigArray, 1024, bigArray, 16, bigArray, 0);
+        }
+    }
+
+    @Test
+    public void testMultiplyMV() {
+        Random rng = new Random();
+        for (int i = 0; i < 20; i++) {
+            array[i] = rng.nextFloat();
+        }
+
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            Matrix.multiplyMV(array, 20, array, 4, array, 0);
+        }
+    }
+
+    @Test
+    public void testMultiplyMVLeftOverlapResult() {
+        Random rng = new Random();
+        for (int i = 0; i < 20; i++) {
+            array[i] = rng.nextFloat();
+        }
+
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            Matrix.multiplyMV(array, 4, array, 4, array, 0);
+        }
+    }
+
+    @Test
+    public void testMultiplyMVRightOverlapResult() {
+        Random rng = new Random();
+        for (int i = 0; i < 32; i++) {
+            array[i] = rng.nextFloat();
+        }
+
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            Matrix.multiplyMV(array, 0, array, 16, array, 0);
+        }
+    }
+
+    @Test
+    public void testMultiplyMVAllOverlap() {
+        Random rng = new Random();
+        for (int i = 0; i < 16; i++) {
+            array[i] = rng.nextFloat();
+        }
+
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            Matrix.multiplyMV(array, 0, array, 0, array, 0);
+        }
+    }
+
+    @Test
+    public void testMultiplyMVOutputBigArray() {
+        Random rng = new Random();
+        for (int i = 0; i < 20; i++) {
+            array[i] = rng.nextFloat();
+        }
+
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            Matrix.multiplyMV(bigArray, 1024, array, 16, array, 0);
+        }
+    }
+
+    @Test
+    public void testMultiplyMVAllBigArray() {
+        Random rng = new Random();
+        for (int i = 0; i < 20; i++) {
+            bigArray[i] = rng.nextFloat();
+        }
+
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            Matrix.multiplyMV(bigArray, 1024, bigArray, 16, bigArray, 0);
+        }
+    }
+
+    @Test
+    public void testTransposeM() {
+        Random rng = new Random();
+        for (int i = 0; i < 16; i++) {
+            array[i] = rng.nextFloat();
+        }
+
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            Matrix.transposeM(array, 16, array, 0);
+        }
+    }
+
+    @Test
+    public void testInvertM() {
+        // non-singular matrix
+        array[ 0] =  0.814f;
+        array[ 1] =  4.976f;
+        array[ 2] = -3.858f;
+        array[ 3] =  7.206f;
+        array[ 4] =  5.112f;
+        array[ 5] = -2.420f;
+        array[ 6] =  8.791f;
+        array[ 7] =  6.426f;
+        array[ 8] =  2.945f;
+        array[ 9] =  1.801f;
+        array[10] = -2.594f;
+        array[11] =  2.663f;
+        array[12] = -5.003f;
+        array[13] = -4.188f;
+        array[14] =  3.340f;
+        array[15] = -1.235f;
+
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            Matrix.invertM(array, 16, array, 0);
+        }
+    }
+
+    @Test
+    public void testOrthoM() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            Matrix.orthoM(array, 0, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f);
+        }
+    }
+
+    @Test
+    public void testFrustumM() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            Matrix.frustumM(array, 0, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f);
+        }
+    }
+
+    @Test
+    public void testPerspectiveM() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            Matrix.perspectiveM(array, 0, 45.0f, 1.0f, 1.0f, 100.0f);
+        }
+    }
+
+    @Test
+    public void testLength() {
+        Random rng = new Random();
+        for (int i = 0; i < 3; i++) {
+            array[i] = rng.nextFloat();
+        }
+
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            Matrix.length(array[0], array[1], array[2]);
+        }
+    }
+
+    @Test
+    public void testSetIdentityM() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            Matrix.setIdentityM(array, 0);
+        }
+    }
+
+    @Test
+    public void testScaleM() {
+        Random rng = new Random();
+        for (int i = 0; i < 19; i++) {
+            array[i] = rng.nextFloat();
+        }
+
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            Matrix.scaleM(array, 19, array, 0, array[16], array[17], array[18]);
+        }
+    }
+
+    @Test
+    public void testScaleMInPlace() {
+        Random rng = new Random();
+        for (int i = 0; i < 19; i++) {
+            array[i] = rng.nextFloat();
+        }
+
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            Matrix.scaleM(array, 0, array[16], array[17], array[18]);
+        }
+    }
+
+    @Test
+    public void testTranslateM() {
+        Random rng = new Random();
+        for (int i = 0; i < 19; i++) {
+            array[i] = rng.nextFloat();
+        }
+
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            Matrix.translateM(array, 19, array, 0, array[16], array[17], array[18]);
+        }
+    }
+
+    @Test
+    public void testTranslateMInPlace() {
+        Random rng = new Random();
+        for (int i = 0; i < 19; i++) {
+            array[i] = rng.nextFloat();
+        }
+
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            Matrix.translateM(array, 0, array[16], array[17], array[18]);
+        }
+    }
+
+    @Test
+    public void testRotateM() {
+        Random rng = new Random();
+        for (int i = 0; i < 20; i++) {
+            array[i] = rng.nextFloat();
+        }
+
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            Matrix.rotateM(array, 20, array, 0, array[16], array[17], array[18], array[19]);
+        }
+    }
+
+    @Test
+    public void testRotateMInPlace() {
+        Random rng = new Random();
+        for (int i = 0; i < 20; i++) {
+            array[i] = rng.nextFloat();
+        }
+
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            Matrix.rotateM(array, 0, array[16], array[17], array[18], array[19]);
+        }
+    }
+
+    @Test
+    public void testSetRotateM() {
+        Random rng = new Random();
+        array[0] = rng.nextFloat() * 90.0f;
+        array[1] = rng.nextFloat() + 0.5f;
+        array[2] = rng.nextFloat() + 0.5f;
+        array[3] = rng.nextFloat() + 0.5f;
+
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            Matrix.setRotateM(array, 4, array[0], array[1], array[2], array[3]);
+        }
+    }
+
+    @Test
+    public void testSetRotateEulerM() {
+        Random rng = new Random();
+        array[0] = rng.nextFloat() * 90.0f;
+        array[1] = rng.nextFloat() * 90.0f;
+        array[2] = rng.nextFloat() * 90.0f;
+
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            Matrix.setRotateEulerM(array, 3, array[0], array[1], array[2]);
+        }
+    }
+
+    @Test
+    public void testSetLookAtM() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            Matrix.setLookAtM(array, 9,
+                    1.0f, 0.0f, 0.0f,
+                    1.0f, 0.0f, 1.0f,
+                    0.0f, 1.0f, 0.0f);
+        }
+    }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 1e13dbf..1775d90 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -882,6 +882,8 @@
                                             // a user-initiated action, it should be fine to just
                                             // put USER instead of UNINSTALL or DISABLED.
                                             cancelJobsForPackageAndUidLocked(pkgName, pkgUid,
+                                                    /* includeSchedulingApp */ true,
+                                                    /* includeSourceApp */ true,
                                                     JobParameters.STOP_REASON_USER,
                                                     JobParameters.INTERNAL_STOP_REASON_UNINSTALL,
                                                     "app disabled");
@@ -932,6 +934,7 @@
                     // get here, but since this is generally a user-initiated action, it should
                     // be fine to just put USER instead of UNINSTALL or DISABLED.
                     cancelJobsForPackageAndUidLocked(pkgName, pkgUid,
+                            /* includeSchedulingApp */ true, /* includeSourceApp */ true,
                             JobParameters.STOP_REASON_USER,
                             JobParameters.INTERNAL_STOP_REASON_UNINSTALL, "app uninstalled");
                     for (int c = 0; c < mControllers.size(); ++c) {
@@ -986,7 +989,12 @@
                         Slog.d(TAG, "Removing jobs for pkg " + pkgName + " at uid " + pkgUid);
                     }
                     synchronized (mLock) {
+                        // Exclude jobs scheduled on behalf of this app for now because SyncManager
+                        // and other job proxy agents may not know to reschedule the job properly
+                        // after force stop.
+                        // TODO(209852664): determine how to best handle syncs & other proxied jobs
                         cancelJobsForPackageAndUidLocked(pkgName, pkgUid,
+                                /* includeSchedulingApp */ true, /* includeSourceApp */ false,
                                 JobParameters.STOP_REASON_USER,
                                 JobParameters.INTERNAL_STOP_REASON_CANCELED,
                                 "app force stopped");
@@ -1304,16 +1312,18 @@
         }
     }
 
+    private final Consumer<JobStatus> mCancelJobDueToUserRemovalConsumer = (toRemove) -> {
+        // There's no guarantee that the process has been stopped by the time we get
+        // here, but since this is a user-initiated action, it should be fine to just
+        // put USER instead of UNINSTALL or DISABLED.
+        cancelJobImplLocked(toRemove, null, JobParameters.STOP_REASON_USER,
+                JobParameters.INTERNAL_STOP_REASON_UNINSTALL, "user removed");
+    };
+
     private void cancelJobsForUserLocked(int userHandle) {
-        final List<JobStatus> jobsForUser = mJobs.getJobsByUser(userHandle);
-        for (int i = 0; i < jobsForUser.size(); i++) {
-            JobStatus toRemove = jobsForUser.get(i);
-            // There's no guarantee that the process has been stopped by the time we get here,
-            // but since this is a user-initiated action, it should be fine to just put USER
-            // instead of UNINSTALL or DISABLED.
-            cancelJobImplLocked(toRemove, null, JobParameters.STOP_REASON_USER,
-                    JobParameters.INTERNAL_STOP_REASON_UNINSTALL, "user removed");
-        }
+        mJobs.forEachJob(
+                (job) -> job.getUserId() == userHandle || job.getSourceUserId() == userHandle,
+                mCancelJobDueToUserRemovalConsumer);
     }
 
     private void cancelJobsForNonExistentUsers() {
@@ -1324,15 +1334,31 @@
     }
 
     private void cancelJobsForPackageAndUidLocked(String pkgName, int uid,
+            boolean includeSchedulingApp, boolean includeSourceApp,
             @JobParameters.StopReason int reason, int internalReasonCode, String debugReason) {
+        if (!includeSchedulingApp && !includeSourceApp) {
+            Slog.wtfStack(TAG,
+                    "Didn't indicate whether to cancel jobs for scheduling and/or source app");
+            includeSourceApp = true;
+        }
         if ("android".equals(pkgName)) {
             Slog.wtfStack(TAG, "Can't cancel all jobs for system package");
             return;
         }
-        final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
+        final List<JobStatus> jobsForUid = new ArrayList<>();
+        if (includeSchedulingApp) {
+            mJobs.getJobsByUid(uid, jobsForUid);
+        }
+        if (includeSourceApp) {
+            mJobs.getJobsBySourceUid(uid, jobsForUid);
+        }
         for (int i = jobsForUid.size() - 1; i >= 0; i--) {
             final JobStatus job = jobsForUid.get(i);
-            if (job.getSourcePackageName().equals(pkgName)) {
+            final boolean shouldCancel =
+                    (includeSchedulingApp
+                            && job.getServiceComponent().getPackageName().equals(pkgName))
+                    || (includeSourceApp && job.getSourcePackageName().equals(pkgName));
+            if (shouldCancel) {
                 cancelJobImplLocked(job, null, reason, internalReasonCode, debugReason);
             }
         }
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
index fcfb45c..78ab06c 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
@@ -22,6 +22,7 @@
 import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
 import static com.android.server.job.JobSchedulerService.sSystemClock;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.job.JobInfo;
 import android.content.ComponentName;
@@ -32,7 +33,6 @@
 import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.SystemClock;
-import android.os.UserHandle;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.util.ArraySet;
@@ -287,26 +287,37 @@
     }
 
     /**
-     * @param userHandle User for whom we are querying the list of jobs.
-     * @return A list of all the jobs scheduled for the provided user. Never null.
+     * @param sourceUid Uid of the source app.
+     * @return A list of all the jobs scheduled for the source app. Never null.
      */
-    public List<JobStatus> getJobsByUser(int userHandle) {
-        return mJobSet.getJobsByUser(userHandle);
+    @NonNull
+    public List<JobStatus> getJobsBySourceUid(int sourceUid) {
+        return mJobSet.getJobsBySourceUid(sourceUid);
+    }
+
+    public void getJobsBySourceUid(int sourceUid, @NonNull List<JobStatus> insertInto) {
+        mJobSet.getJobsBySourceUid(sourceUid, insertInto);
     }
 
     /**
      * @param uid Uid of the requesting app.
      * @return All JobStatus objects for a given uid from the master list. Never null.
      */
+    @NonNull
     public List<JobStatus> getJobsByUid(int uid) {
         return mJobSet.getJobsByUid(uid);
     }
 
+    public void getJobsByUid(int uid, @NonNull List<JobStatus> insertInto) {
+        mJobSet.getJobsByUid(uid, insertInto);
+    }
+
     /**
      * @param uid Uid of the requesting app.
      * @param jobId Job id, specified at schedule-time.
      * @return the JobStatus that matches the provided uId and jobId, or null if none found.
      */
+    @Nullable
     public JobStatus getJobByUidAndJobId(int uid, int jobId) {
         return mJobSet.get(uid, jobId);
     }
@@ -1220,27 +1231,31 @@
 
         public List<JobStatus> getJobsByUid(int uid) {
             ArrayList<JobStatus> matchingJobs = new ArrayList<JobStatus>();
-            ArraySet<JobStatus> jobs = mJobs.get(uid);
-            if (jobs != null) {
-                matchingJobs.addAll(jobs);
-            }
+            getJobsByUid(uid, matchingJobs);
             return matchingJobs;
         }
 
-        // By user, not by uid, so we need to traverse by key and check
-        public List<JobStatus> getJobsByUser(int userId) {
-            final ArrayList<JobStatus> result = new ArrayList<JobStatus>();
-            for (int i = mJobsPerSourceUid.size() - 1; i >= 0; i--) {
-                if (UserHandle.getUserId(mJobsPerSourceUid.keyAt(i)) == userId) {
-                    final ArraySet<JobStatus> jobs = mJobsPerSourceUid.valueAt(i);
-                    if (jobs != null) {
-                        result.addAll(jobs);
-                    }
-                }
+        public void getJobsByUid(int uid, List<JobStatus> insertInto) {
+            ArraySet<JobStatus> jobs = mJobs.get(uid);
+            if (jobs != null) {
+                insertInto.addAll(jobs);
             }
+        }
+
+        @NonNull
+        public List<JobStatus> getJobsBySourceUid(int sourceUid) {
+            final ArrayList<JobStatus> result = new ArrayList<JobStatus>();
+            getJobsBySourceUid(sourceUid, result);
             return result;
         }
 
+        public void getJobsBySourceUid(int sourceUid, List<JobStatus> insertInto) {
+            final ArraySet<JobStatus> jobs = mJobsPerSourceUid.get(sourceUid);
+            if (jobs != null) {
+                insertInto.addAll(jobs);
+            }
+        }
+
         public boolean add(JobStatus job) {
             final int uid = job.getUid();
             final int sourceUid = job.getSourceUid();
@@ -1382,7 +1397,7 @@
         }
 
         public void forEachJob(@Nullable Predicate<JobStatus> filterPredicate,
-                Consumer<JobStatus> functor) {
+                @NonNull Consumer<JobStatus> functor) {
             for (int uidIndex = mJobs.size() - 1; uidIndex >= 0; uidIndex--) {
                 ArraySet<JobStatus> jobs = mJobs.valueAt(uidIndex);
                 if (jobs != null) {
diff --git a/core/api/current.txt b/core/api/current.txt
index 9a76523..e30761f 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -18646,8 +18646,10 @@
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.hardware.lights.Light> CREATOR;
     field public static final int LIGHT_CAPABILITY_BRIGHTNESS = 1; // 0x1
-    field public static final int LIGHT_CAPABILITY_RGB = 0; // 0x0
+    field public static final int LIGHT_CAPABILITY_COLOR_RGB = 2; // 0x2
+    field @Deprecated public static final int LIGHT_CAPABILITY_RGB = 0; // 0x0
     field public static final int LIGHT_TYPE_INPUT = 10001; // 0x2711
+    field public static final int LIGHT_TYPE_KEYBOARD_BACKLIGHT = 10003; // 0x2713
     field public static final int LIGHT_TYPE_MICROPHONE = 8; // 0x8
     field public static final int LIGHT_TYPE_PLAYER_ID = 10002; // 0x2712
   }
@@ -22436,6 +22438,7 @@
     field public static final String MIMETYPE_AUDIO_SCRAMBLED = "audio/scrambled";
     field public static final String MIMETYPE_AUDIO_VORBIS = "audio/vorbis";
     field public static final String MIMETYPE_IMAGE_ANDROID_HEIC = "image/vnd.android.heic";
+    field public static final String MIMETYPE_IMAGE_AVIF = "image/avif";
     field public static final String MIMETYPE_TEXT_CEA_608 = "text/cea-608";
     field public static final String MIMETYPE_TEXT_CEA_708 = "text/cea-708";
     field public static final String MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index cf5f10b..7a9f3c1 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -6662,6 +6662,9 @@
 
         // Pass the current context to HardwareRenderer
         HardwareRenderer.setContextForInit(getSystemContext());
+        if (data.persistent) {
+            HardwareRenderer.setIsSystemOrPersistent();
+        }
 
         // Instrumentation info affects the class loader, so load it before
         // setting up the app context.
diff --git a/core/java/android/content/res/ResourceTimer.java b/core/java/android/content/res/ResourceTimer.java
index 13f0b72..d51f64c 100644
--- a/core/java/android/content/res/ResourceTimer.java
+++ b/core/java/android/content/res/ResourceTimer.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 
+import android.app.AppProtoEnums;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -30,6 +31,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.FastPrintWriter;
+import com.android.internal.util.FrameworkStatsLog;
 
 import java.io.FileOutputStream;
 import java.io.PrintWriter;
@@ -111,6 +113,14 @@
     private static Config sConfig;
 
     /**
+     * This array contains the statsd enum associated with each timer entry.  A value of NONE (0)
+     * means that the entry should not be logged to statsd.  (This would be the case for timers
+     * that are created for temporary debugging.)
+     */
+    @GuardedBy("sLock")
+    private static int[] sApiMap;
+
+    /**
      * A singleton Summary object that is refilled from the native side.  The length of the array
      * is the number of timers that can be fetched.  nativeGetTimers() will fill the array to the
      * smaller of the length of the array or the actual number of timers in the runtime.  The
@@ -165,6 +175,19 @@
                 sTimers[i].percentile = new int[sConfig.maxBuckets];
                 sTimers[i].largest = new int[sConfig.maxLargest];
             }
+            // Map the values returned from the runtime to statsd enumerals  The runtime may
+            // return timers that are not meant to be logged via statsd.  Such timers are mapped
+            // to RESOURCE_API_NONE.
+            sApiMap = new int[sConfig.maxTimer];
+            for (int i = 0; i < sApiMap.length; i++) {
+                if (sConfig.timers[i].equals("GetResourceValue")) {
+                    sApiMap[i] = AppProtoEnums.RESOURCE_API_GET_VALUE;
+                } else if (sConfig.timers[i].equals("RetrieveAttributes")) {
+                    sApiMap[i] = AppProtoEnums.RESOURCE_API_RETRIEVE_ATTRIBUTES;
+                } else {
+                    sApiMap[i] = AppProtoEnums.RESOURCE_API_NONE;
+                }
+            }
 
             sCurrentPoint = 0;
             startTimer();
@@ -194,7 +217,9 @@
             delay = sPublicationPoints[sCurrentPoint];
         } else {
             // Repeat with the final publication point.
-            delay = sCurrentPoint * sPublicationPoints[sPublicationPoints.length - 1];
+            final long repeated = sPublicationPoints[sPublicationPoints.length - 1];
+            final int prelude = sPublicationPoints.length - 1;
+            delay = (sCurrentPoint - prelude) * repeated;
         }
         // Convert minutes to milliseconds.
         delay *= 60 * 1000;
@@ -223,10 +248,19 @@
         update(true);
         // Log the number of records read.  This happens a few times a day.
         for (int i = 0; i < sTimers.length; i++) {
-            if (sTimers[i].count > 0) {
+            var timer = sTimers[i];
+            if (timer.count > 0) {
                 Log.i(TAG, TextUtils.formatSimple("%s count=%d pvalues=%s",
-                                sConfig.timers[i], sTimers[i].count,
-                                packedString(sTimers[i].percentile)));
+                                sConfig.timers[i], timer.count, packedString(timer.percentile)));
+                if (sApiMap[i] != AppProtoEnums.RESOURCE_API_NONE) {
+                    FrameworkStatsLog.write(FrameworkStatsLog.RESOURCE_API_INFO,
+                            sApiMap[i],
+                            timer.count, timer.total,
+                            timer.percentile[0], timer.percentile[1],
+                            timer.percentile[2], timer.percentile[3],
+                            timer.largest[0], timer.largest[1], timer.largest[2],
+                            timer.largest[3], timer.largest[4]);
+                }
             }
         }
         sCurrentPoint++;
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index 9e87037..90e92db 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -159,8 +159,9 @@
      *
      * <li> For a SurfaceView output surface, the timestamp base is {@link
      * #TIMESTAMP_BASE_CHOREOGRAPHER_SYNCED}. The timestamp is overridden with choreographer
-     * pulses from the display subsystem for smoother display of camera frames. The timestamp
-     * is roughly in the same time base as {@link android.os.SystemClock#uptimeMillis}.</li>
+     * pulses from the display subsystem for smoother display of camera frames when the camera
+     * device runs in fixed frame rate. The timestamp is roughly in the same time base as
+     * {@link android.os.SystemClock#uptimeMillis}.</li>
      * <li> For an output surface of MediaRecorder, MediaCodec, or ImageReader with {@link
      * android.hardware.HardwareBuffer#USAGE_VIDEO_ENCODE} usge flag, the timestamp base is
      * {@link #TIMESTAMP_BASE_MONOTONIC}, which is roughly the same time base as
@@ -231,7 +232,8 @@
      *
      * <p>The timestamp of the output images are overridden with choreographer pulses from the
      * display subsystem for smoother display of camera frames. An output target of SurfaceView
-     * uses this time base by default.</p>
+     * uses this time base by default. Note that the timestamp override is done for fixed camera
+     * frame rate only.</p>
      *
      * <p>This timestamp base isn't applicable to SurfaceTexture targets. SurfaceTexture's
      * {@link android.graphics.SurfaceTexture#updateTexImage updateTexImage} function always
diff --git a/core/java/android/hardware/lights/Light.java b/core/java/android/hardware/lights/Light.java
index c311379..1df9b75 100644
--- a/core/java/android/hardware/lights/Light.java
+++ b/core/java/android/hardware/lights/Light.java
@@ -60,15 +60,29 @@
     public static final int LIGHT_TYPE_PLAYER_ID = 10002;
 
     /**
+     * Type for lights that illuminate keyboard keys.
+     */
+    public static final int LIGHT_TYPE_KEYBOARD_BACKLIGHT = 10003;
+
+    /**
      * Capability for lights that could adjust its LED brightness. If the capability is not present
-     * the led can only be turned either on or off.
+     * the LED can only be turned either on or off.
      */
     public static final int LIGHT_CAPABILITY_BRIGHTNESS = 1 << 0;
 
     /**
-     * Capability for lights that has red, green and blue LEDs to control the light's color.
+     * Capability for lights that have red, green and blue LEDs to control the light's color.
      */
-    public static final int LIGHT_CAPABILITY_RGB = 0 << 1;
+    public static final int LIGHT_CAPABILITY_COLOR_RGB = 1 << 1;
+
+    /**
+     * Capability for lights that have red, green and blue LEDs to control the light's color.
+     *
+     * @deprecated Wrong int based flag with value 0. Use capability flag {@code
+     * LIGHT_CAPABILITY_COLOR_RGB} instead.
+     */
+    @Deprecated
+    public static final int LIGHT_CAPABILITY_RGB = 0;
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
@@ -77,6 +91,7 @@
             LIGHT_TYPE_MICROPHONE,
             LIGHT_TYPE_INPUT,
             LIGHT_TYPE_PLAYER_ID,
+            LIGHT_TYPE_KEYBOARD_BACKLIGHT,
         })
     public @interface LightType {}
 
@@ -85,6 +100,7 @@
     @IntDef(flag = true, prefix = {"LIGHT_CAPABILITY_"},
         value = {
             LIGHT_CAPABILITY_BRIGHTNESS,
+            LIGHT_CAPABILITY_COLOR_RGB,
             LIGHT_CAPABILITY_RGB,
         })
     public @interface LightCapability {}
@@ -233,7 +249,7 @@
      * @return True if the hardware can control the RGB led, otherwise false.
      */
     public boolean hasRgbControl() {
-        return (mCapabilities & LIGHT_CAPABILITY_RGB) == LIGHT_CAPABILITY_RGB;
+        return (mCapabilities & LIGHT_CAPABILITY_COLOR_RGB) == LIGHT_CAPABILITY_COLOR_RGB;
     }
 
 }
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 8b3451e..92088e9 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -65,6 +65,7 @@
 import android.annotation.UiContext;
 import android.app.ActivityManager;
 import android.app.Dialog;
+import android.app.compat.CompatChanges;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledSince;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -546,6 +547,20 @@
     @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
     public static final long FINISH_INPUT_NO_FALLBACK_CONNECTION = 156215187L; // This is a bug id.
 
+    /**
+     * Disallow IMEs to override {@link InputMethodService#onCreateInputMethodSessionInterface()}
+     * method.
+     *
+     * <p>If IMEs targeting on Android U and beyond override the
+     * {@link InputMethodService#onCreateInputMethodSessionInterface()}, an {@link LinkageError}
+     * would be thrown.</p>
+     *
+     * @hide
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    private static final long DISALLOW_INPUT_METHOD_INTERFACE_OVERRIDE = 148086656L;
+
     LayoutInflater mInflater;
     TypedArray mThemeAttrs;
     @UnsupportedAppUsage
@@ -1527,6 +1542,11 @@
     }
 
     @Override public void onCreate() {
+        if (methodIsOverridden("onCreateInputMethodSessionInterface")
+                && CompatChanges.isChangeEnabled(DISALLOW_INPUT_METHOD_INTERFACE_OVERRIDE)) {
+            throw new LinkageError("InputMethodService#onCreateInputMethodSessionInterface()"
+                    + " can no longer be overridden!");
+        }
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.onCreate");
         mTheme = Resources.selectSystemTheme(mTheme,
                 getApplicationInfo().targetSdkVersion,
@@ -1764,6 +1784,9 @@
      * {@link InputMethodService#onDisplayCompletions(CompletionInfo[])},
      * {@link InputMethodService#onUpdateExtractedText(int, ExtractedText)},
      * {@link InputMethodService#onUpdateSelection(int, int, int, int, int, int)} instead.
+     *
+     * <p>IMEs targeting on Android U and above cannot override this method, or an
+     * {@link LinkageError} would be thrown.</p>
      */
     @Deprecated
     @Override
@@ -4067,4 +4090,13 @@
         final KeyEvent upEvent = createBackKeyEvent(KeyEvent.ACTION_UP, hasStartedTracking);
         onKeyUp(KeyEvent.KEYCODE_BACK, upEvent);
     }
+
+    private boolean methodIsOverridden(String methodName, Class<?>... parameterTypes) {
+        try {
+            return getClass().getMethod(methodName, parameterTypes).getDeclaringClass()
+                    != InputMethodService.class;
+        } catch (NoSuchMethodException e) {
+            throw new RuntimeException("Method must exist.", e);
+        }
+    }
 }
diff --git a/core/java/android/service/credentials/Action.java b/core/java/android/service/credentials/Action.java
new file mode 100644
index 0000000..186b2a6
--- /dev/null
+++ b/core/java/android/service/credentials/Action.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2022 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 android.service.credentials;
+
+import android.app.PendingIntent;
+import android.app.slice.Slice;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import java.util.Objects;
+
+/**
+ * An action defined by the provider that intents into the provider's app for specific
+ * user actions.
+ *
+ * @hide
+ */
+public final class Action implements Parcelable {
+    /** Info to be displayed with this action on the UI. */
+    private final @NonNull Slice mInfo;
+    /**
+     * The pending intent to be invoked when the user selects this action.
+     */
+    private final @NonNull PendingIntent mPendingIntent;
+
+    /**
+     * Constructs an action to be displayed on the UI.
+     *
+     * @param actionInfo The info to be displayed along with this action.
+     * @param pendingIntent The intent to be invoked when the user selects this action.
+     * @throws NullPointerException If {@code actionInfo}, or {@code pendingIntent} is null.
+     */
+    public Action(@NonNull Slice actionInfo, @NonNull PendingIntent pendingIntent) {
+        Objects.requireNonNull(actionInfo, "actionInfo must not be null");
+        Objects.requireNonNull(pendingIntent, "pendingIntent must not be null");
+        mInfo = actionInfo;
+        mPendingIntent = pendingIntent;
+    }
+
+    private Action(@NonNull Parcel in) {
+        mInfo = in.readParcelable(Slice.class.getClassLoader(), Slice.class);
+        mPendingIntent = in.readParcelable(PendingIntent.class.getClassLoader(),
+                PendingIntent.class);
+    }
+
+    public static final @NonNull Creator<Action> CREATOR = new Creator<Action>() {
+        @Override
+        public Action createFromParcel(@NonNull Parcel in) {
+            return new Action(in);
+        }
+
+        @Override
+        public Action[] newArray(int size) {
+            return new Action[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        mInfo.writeToParcel(dest, flags);
+        mPendingIntent.writeToParcel(dest, flags);
+    }
+
+    /**
+     * Returns the action info as a {@link Slice} object, to be displayed on the UI.
+     */
+    public @NonNull Slice getActionInfo() {
+        return mInfo;
+    }
+
+    /**
+     * Returns the {@link PendingIntent} to be invoked when the action is selected.
+     */
+    public @NonNull PendingIntent getPendingIntent() {
+        return mPendingIntent;
+    }
+}
diff --git a/core/java/android/service/credentials/CreateCredentialCallback.java b/core/java/android/service/credentials/CreateCredentialCallback.java
new file mode 100644
index 0000000..6108eea
--- /dev/null
+++ b/core/java/android/service/credentials/CreateCredentialCallback.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2022 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 android.service.credentials;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * Callback to be invoked as a response to {@link CreateCredentialRequest}.
+ *
+ * @hide
+ */
+public final class CreateCredentialCallback {
+    private static final String TAG = "CreateCredentialCallback";
+
+    private final ICreateCredentialCallback mCallback;
+
+    /** @hide */
+    public CreateCredentialCallback(@NonNull ICreateCredentialCallback callback) {
+        mCallback = callback;
+    }
+
+    /**
+     * Invoked on a successful response for {@link CreateCredentialRequest}
+     * @param response The response from the credential provider.
+     */
+    public void onSuccess(@NonNull CreateCredentialResponse response) {
+        try {
+            mCallback.onSuccess(response);
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Invoked on a failure response for {@link CreateCredentialRequest}
+     * @param errorCode The code defining the type of error.
+     * @param message The message corresponding to the failure.
+     */
+    public void onFailure(int errorCode, @Nullable CharSequence message) {
+        Log.w(TAG, "onFailure: " + message);
+        try {
+            mCallback.onFailure(errorCode, message);
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
+        }
+    }
+}
diff --git a/core/java/android/service/credentials/CreateCredentialRequest.aidl b/core/java/android/service/credentials/CreateCredentialRequest.aidl
new file mode 100644
index 0000000..eb7fba9
--- /dev/null
+++ b/core/java/android/service/credentials/CreateCredentialRequest.aidl
@@ -0,0 +1,3 @@
+package android.service.credentials;
+
+parcelable CreateCredentialRequest;
\ No newline at end of file
diff --git a/core/java/android/service/credentials/CreateCredentialRequest.java b/core/java/android/service/credentials/CreateCredentialRequest.java
new file mode 100644
index 0000000..ac11e04b
--- /dev/null
+++ b/core/java/android/service/credentials/CreateCredentialRequest.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2022 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 android.service.credentials;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+
+/**
+ * Request for creating a credential.
+ *
+ * @hide
+ */
+public final class CreateCredentialRequest implements Parcelable {
+    private final @NonNull String mCallingPackage;
+    private final @NonNull String mType;
+    private final @NonNull Bundle mData;
+
+    /**
+     * Constructs a new instance.
+     *
+     * @throws IllegalArgumentException If {@code callingPackage}, or {@code type} string is
+     * null or empty.
+     * @throws NullPointerException If {@code data} is null.
+     */
+    public CreateCredentialRequest(@NonNull String callingPackage,
+            @NonNull String type, @NonNull Bundle data) {
+        mCallingPackage = Preconditions.checkStringNotEmpty(callingPackage,
+                "callingPackage must not be null or empty");
+        mType = Preconditions.checkStringNotEmpty(type,
+                "type must not be null or empty");
+        mData = Objects.requireNonNull(data, "data must not be null");
+    }
+
+    private CreateCredentialRequest(@NonNull Parcel in) {
+        mCallingPackage = in.readString8();
+        mType = in.readString8();
+        mData = in.readBundle();
+    }
+
+    public static final @NonNull Creator<CreateCredentialRequest> CREATOR =
+            new Creator<CreateCredentialRequest>() {
+                @Override
+                public CreateCredentialRequest createFromParcel(@NonNull Parcel in) {
+                    return new CreateCredentialRequest(in);
+                }
+
+                @Override
+                public CreateCredentialRequest[] newArray(int size) {
+                    return new CreateCredentialRequest[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString8(mCallingPackage);
+        dest.writeString8(mType);
+        dest.writeBundle(mData);
+    }
+
+    /** Returns the calling package of the calling app. */
+    @NonNull
+    public String getCallingPackage() {
+        return mCallingPackage;
+    }
+
+    /** Returns the type of the credential to be created. */
+    @NonNull
+    public String getType() {
+        return mType;
+    }
+
+    /** Returns the data to be used while creating the credential. */
+    @NonNull
+    public Bundle getData() {
+        return mData;
+    }
+}
diff --git a/core/java/android/service/credentials/CreateCredentialResponse.aidl b/core/java/android/service/credentials/CreateCredentialResponse.aidl
new file mode 100644
index 0000000..73c9147
--- /dev/null
+++ b/core/java/android/service/credentials/CreateCredentialResponse.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2022 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 android.service.credentials;
+
+parcelable CreateCredentialResponse;
diff --git a/core/java/android/service/credentials/CreateCredentialResponse.java b/core/java/android/service/credentials/CreateCredentialResponse.java
new file mode 100644
index 0000000..f2ad7272
--- /dev/null
+++ b/core/java/android/service/credentials/CreateCredentialResponse.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2022 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 android.service.credentials;
+
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Response to a {@link CreateCredentialRequest}.
+ *
+ * @hide
+ */
+public final class CreateCredentialResponse implements Parcelable {
+    private final @Nullable CharSequence mHeader;
+    private final @NonNull List<SaveEntry> mSaveEntries;
+
+    private CreateCredentialResponse(@NonNull Parcel in) {
+        mHeader = in.readCharSequence();
+        mSaveEntries = in.createTypedArrayList(SaveEntry.CREATOR);
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeCharSequence(mHeader);
+        dest.writeTypedList(mSaveEntries);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final @NonNull Creator<CreateCredentialResponse> CREATOR =
+            new Creator<CreateCredentialResponse>() {
+                @Override
+                public CreateCredentialResponse createFromParcel(@NonNull Parcel in) {
+                    return new CreateCredentialResponse(in);
+                }
+
+                @Override
+                public CreateCredentialResponse[] newArray(int size) {
+                    return new CreateCredentialResponse[size];
+                }
+            };
+
+    /* package-private */ CreateCredentialResponse(
+            @Nullable CharSequence header,
+            @NonNull List<SaveEntry> saveEntries) {
+        this.mHeader = header;
+        this.mSaveEntries = saveEntries;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mSaveEntries);
+    }
+
+    /** Returns the header to be displayed on the UI. */
+    public @Nullable CharSequence getHeader() {
+        return mHeader;
+    }
+
+    /** Returns the list of save entries to be displayed on the UI. */
+    public @NonNull List<SaveEntry> getSaveEntries() {
+        return mSaveEntries;
+    }
+
+    /**
+     * A builder for {@link CreateCredentialResponse}
+     */
+    @SuppressWarnings("WeakerAccess")
+    public static final class Builder {
+
+        private @Nullable CharSequence mHeader;
+        private @NonNull List<SaveEntry> mSaveEntries = new ArrayList<>();
+
+        /** Sets the header to be displayed on the UI. */
+        public @NonNull Builder setHeader(@Nullable CharSequence header) {
+            mHeader = header;
+            return this;
+        }
+
+        /**
+         * Sets the list of save entries to be shown on the UI.
+         *
+         * @throws IllegalArgumentException If {@code saveEntries} is empty.
+         * @throws NullPointerException If {@code saveEntries} is null, or any of its elements
+         * are null.
+         */
+        public @NonNull Builder setSaveEntries(@NonNull List<SaveEntry> saveEntries) {
+            Preconditions.checkCollectionNotEmpty(saveEntries, "saveEntries");
+            mSaveEntries = Preconditions.checkCollectionElementsNotNull(
+                    saveEntries, "saveEntries");
+            return this;
+        }
+
+        /**
+         * Adds an entry to the list of save entries to be shown on the UI.
+         *
+         * @throws NullPointerException If {@code saveEntry} is null.
+         */
+        public @NonNull Builder addSaveEntry(@NonNull SaveEntry saveEntry) {
+            mSaveEntries.add(Objects.requireNonNull(saveEntry));
+            return this;
+        }
+
+        /**
+         * Builds the instance.
+         *
+         * @throws IllegalArgumentException If {@code saveEntries} is empty.
+         */
+        public @NonNull CreateCredentialResponse build() {
+            Preconditions.checkCollectionNotEmpty(mSaveEntries, "saveEntries must "
+                    + "not be empty");
+            return new CreateCredentialResponse(
+                    mHeader,
+                    mSaveEntries);
+        }
+    }
+}
diff --git a/core/java/android/service/credentials/Credential.java b/core/java/android/service/credentials/Credential.java
new file mode 100644
index 0000000..7d5da8a
--- /dev/null
+++ b/core/java/android/service/credentials/Credential.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2022 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 android.service.credentials;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import static java.util.Objects.requireNonNull;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A Credential object that contains type specific data that is returned from the credential
+ * provider to the framework. Framework then converts it to an app facing representation and
+ * returns to the calling app.
+ *
+ * @hide
+ */
+public final class Credential implements Parcelable {
+    /** The type of this credential. */
+    private final @NonNull String mType;
+
+    /** The data associated with this credential. */
+    private final @NonNull Bundle mData;
+
+    /**
+     * Constructs a credential object.
+     *
+     * @param type The type of the credential.
+     * @param data The data of the credential that is passed back to the framework, and eventually
+     *             to the calling app.
+     * @throws NullPointerException If {@code data} is null.
+     * @throws IllegalArgumentException If {@code type} is null or empty.
+     */
+    public Credential(@NonNull String type, @NonNull Bundle data) {
+        Preconditions.checkStringNotEmpty(type, "type must not be null, or empty");
+        requireNonNull(data, "data must not be null");
+        this.mType = type;
+        this.mData = data;
+    }
+
+    private Credential(@NonNull Parcel in) {
+        mType = in.readString16NoHelper();
+        mData = in.readBundle();
+    }
+
+    /**
+     * Returns the type of the credential.
+     */
+    public @NonNull String getType() {
+        return mType;
+    }
+
+    /**
+     * Returns the data associated with the credential.
+     */
+    public @NonNull Bundle getData() {
+        return mData;
+    }
+
+    public static final @NonNull Creator<Credential> CREATOR = new Creator<Credential>() {
+        @Override
+        public Credential createFromParcel(@NonNull Parcel in) {
+            return new Credential(in);
+        }
+
+        @Override
+        public Credential[] newArray(int size) {
+            return new Credential[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString8(mType);
+        dest.writeBundle(mData);
+    }
+}
diff --git a/core/java/android/service/credentials/CredentialEntry.java b/core/java/android/service/credentials/CredentialEntry.java
new file mode 100644
index 0000000..b49215a
--- /dev/null
+++ b/core/java/android/service/credentials/CredentialEntry.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2022 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 android.service.credentials;
+
+import android.annotation.Nullable;
+import android.app.PendingIntent;
+import android.app.slice.Slice;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+
+/**
+ * A credential entry that is displayed on the account selector UI. Each entry corresponds to
+ * something that the user can select.
+ *
+ * @hide
+ */
+public final class CredentialEntry implements Parcelable {
+    /** The type of the credential entry to be shown on the UI. */
+    private final @NonNull String mType;
+
+    /** The info to be displayed along with this credential entry on the UI. */
+    private final @NonNull Slice mInfo;
+
+    /** The pending intent to be invoked when this credential entry is selected. */
+    private final @Nullable PendingIntent mPendingIntent;
+
+    /**
+     * The underlying credential to be returned to the app when the user selects
+     * this credential entry.
+     */
+    private final @Nullable Credential mCredential;
+
+    /** A flag denoting whether auto-select is enabled for this entry. */
+    private final @NonNull boolean mAutoSelectAllowed;
+
+    private CredentialEntry(@NonNull String type, @NonNull Slice entryInfo,
+            @Nullable PendingIntent pendingIntent, @Nullable Credential credential,
+            @NonNull boolean autoSeletAllowed) {
+        mType = type;
+        mInfo = entryInfo;
+        mPendingIntent = pendingIntent;
+        mCredential = credential;
+        mAutoSelectAllowed = autoSeletAllowed;
+    }
+
+    private CredentialEntry(@NonNull Parcel in) {
+        mType = in.readString();
+        mInfo = in.readParcelable(Slice.class.getClassLoader(), Slice.class);
+        mPendingIntent = in.readParcelable(PendingIntent.class.getClassLoader(),
+                PendingIntent.class);
+        mCredential = in.readParcelable(Credential.class.getClassLoader(),
+                Credential.class);
+        mAutoSelectAllowed = in.readBoolean();
+    }
+
+    public static final @NonNull Creator<CredentialEntry> CREATOR =
+            new Creator<CredentialEntry>() {
+        @Override
+        public CredentialEntry createFromParcel(@NonNull Parcel in) {
+            return new CredentialEntry(in);
+        }
+
+        @Override
+        public CredentialEntry[] newArray(int size) {
+            return new CredentialEntry[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString8(mType);
+        mInfo.writeToParcel(dest, flags);
+        mPendingIntent.writeToParcel(dest, flags);
+        mCredential.writeToParcel(dest, flags);
+        dest.writeBoolean(mAutoSelectAllowed);
+    }
+
+    /**
+     * Returns the specific credential type of the entry.
+     */
+    public @NonNull String getType() {
+        return mType;
+    }
+
+    /**
+     * Returns the UI info to be displayed for this entry.
+     */
+    public @NonNull Slice getInfo() {
+        return mInfo;
+    }
+
+    /**
+     * Returns the pending intent to be invoked if the user selects this entry.
+     */
+    public @Nullable PendingIntent getPendingIntent() {
+        return mPendingIntent;
+    }
+
+    /**
+     * Returns the credential associated with this entry.
+     */
+    public @Nullable Credential getCredential() {
+        return mCredential;
+    }
+
+    /**
+     * Returns whether this entry can be auto selected if it is the only option for the user.
+     */
+    public @NonNull boolean isAutoSelectAllowed() {
+        return mAutoSelectAllowed;
+    }
+
+    /**
+     * Builder for {@link CredentialEntry}.
+     */
+    public static final class Builder {
+        private String mType;
+        private Slice mInfo;
+        private PendingIntent mPendingIntent;
+        private Credential mCredential;
+        private boolean mAutoSelectAllowed = false;
+
+        /**
+         * Builds the instance.
+         * @param type The type of credential underlying this credential entry.
+         * @param info The info to be displayed with this entry on the UI.
+         *
+         * @throws IllegalArgumentException If {@code type} is null or empty.
+         * @throws NullPointerException If {@code info} is null.
+         */
+        public Builder(@NonNull String type, @NonNull Slice info) {
+            mType = Preconditions.checkStringNotEmpty(type, "type must not be "
+                    + "null, or empty");
+            mInfo = Objects.requireNonNull(info, "info must not be null");
+        }
+
+        /**
+         * Sets the pendingIntent to be invoked if the user selects this entry.
+         *
+         * @throws IllegalStateException If {@code credential} is already set. Must either set the
+         * {@code credential}, or the {@code pendingIntent}.
+         */
+        public @NonNull Builder setPendingIntent(@Nullable PendingIntent pendingIntent) {
+            Preconditions.checkState(pendingIntent != null && mCredential != null,
+                    "credential is already set. Cannot set both the pendingIntent "
+                            + "and the credential");
+            mPendingIntent = pendingIntent;
+            return this;
+        }
+
+        /**
+         * Sets the credential to be used, if the user selects this entry.
+         *
+         * @throws IllegalStateException If {@code pendingIntent} is already set. Must either set
+         * the {@code pendingIntent}, or the {@code credential}.
+         */
+        public @NonNull Builder setCredential(@Nullable Credential credential) {
+            Preconditions.checkState(credential != null && mPendingIntent != null,
+                    "pendingIntent is already set. Cannot set both the "
+                            + "pendingIntent and the credential");
+            mCredential = credential;
+            return this;
+        }
+
+        /**
+         * Sets whether the entry is allowed to be auto selected by the framework.
+         * The default value is set to false.
+         */
+        public @NonNull Builder setAutoSelectAllowed(@NonNull boolean autoSelectAllowed) {
+            mAutoSelectAllowed = autoSelectAllowed;
+            return this;
+        }
+
+        /**
+         * Creates a new {@link CredentialEntry} instance.
+         *
+         * @throws NullPointerException If {@code info} is null.
+         * @throws IllegalArgumentException If {@code type} is null, or empty.
+         * @throws IllegalStateException If neither {@code pendingIntent} nor {@code credential}
+         * is set, or if both are set.
+         */
+        public @NonNull CredentialEntry build() {
+            Preconditions.checkState(mPendingIntent == null && mCredential == null,
+                    "Either pendingIntent or credential must be set");
+            Preconditions.checkState(mPendingIntent != null && mCredential != null,
+                    "Cannot set both the pendingIntent and credential");
+            return new CredentialEntry(mType, mInfo, mPendingIntent,
+                    mCredential, mAutoSelectAllowed);
+        }
+    }
+}
diff --git a/core/java/android/service/credentials/CredentialProviderService.java b/core/java/android/service/credentials/CredentialProviderService.java
new file mode 100644
index 0000000..1fe89df
--- /dev/null
+++ b/core/java/android/service/credentials/CredentialProviderService.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2022 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 android.service.credentials;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.app.Service;
+import android.content.Intent;
+import android.os.CancellationSignal;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.ICancellationSignal;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.Objects;
+
+/**
+ * Main service to be extended by credential providers, in order to return user credentials
+ * to the framework.
+ *
+ * @hide
+ */
+public abstract class CredentialProviderService extends Service {
+    private static final String TAG = "CredProviderService";
+    private Handler mHandler;
+
+    public static final String SERVICE_INTERFACE =
+            "android.service.credentials.CredentialProviderService";
+
+    @CallSuper
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mHandler = new Handler(Looper.getMainLooper(), null, true);
+    }
+
+    @Override
+    public final @NonNull IBinder onBind(@NonNull Intent intent) {
+        if (SERVICE_INTERFACE.equals(intent.getAction())) {
+            return mInterface.asBinder();
+        }
+        Log.i(TAG, "Failed to bind with intent: " + intent);
+        return null;
+    }
+
+    private final ICredentialProviderService mInterface = new ICredentialProviderService.Stub() {
+        @Override
+        public void onGetCredentials(GetCredentialsRequest request, ICancellationSignal transport,
+                IGetCredentialsCallback callback) throws RemoteException {
+            Objects.requireNonNull(request);
+            Objects.requireNonNull(transport);
+            Objects.requireNonNull(callback);
+
+            mHandler.sendMessage(obtainMessage(
+                    CredentialProviderService::onGetCredentials,
+                    CredentialProviderService.this, request,
+                    CancellationSignal.fromTransport(transport),
+                    new GetCredentialsCallback(callback)
+            ));
+        }
+
+        @Override
+        public void onCreateCredential(CreateCredentialRequest request,
+                ICancellationSignal transport, ICreateCredentialCallback callback)
+                throws RemoteException {
+            Objects.requireNonNull(request);
+            Objects.requireNonNull(transport);
+            Objects.requireNonNull(callback);
+
+            mHandler.sendMessage(obtainMessage(
+                    CredentialProviderService::onCreateCredential,
+                    CredentialProviderService.this, request,
+                    CancellationSignal.fromTransport(transport),
+                    new CreateCredentialCallback(callback)
+            ));
+        }
+    };
+
+    /**
+     * Called by the android system to retrieve user credentials from the connected provider
+     * service.
+     * @param request The credential request for the provider to handle.
+     * @param cancellationSignal Signal for providers to listen to any cancellation requests from
+     *                           the android system.
+     * @param callback Object used to relay the response of the credentials request.
+     */
+    public abstract void onGetCredentials(@NonNull GetCredentialsRequest request,
+            @NonNull CancellationSignal cancellationSignal,
+            @NonNull GetCredentialsCallback callback);
+
+    /**
+     * Called by the android system to create a credential.
+     * @param request The credential creation request for the provider to handle.
+     * @param cancellationSignal Signal for providers to listen to any cancellation requests from
+     *                           the android system.
+     * @param callback Object used to relay the response of the credential creation request.
+     */
+    public abstract void onCreateCredential(@NonNull CreateCredentialRequest request,
+            @NonNull CancellationSignal cancellationSignal,
+            @NonNull CreateCredentialCallback callback);
+}
diff --git a/core/java/android/service/credentials/CredentialsDisplayContent.java b/core/java/android/service/credentials/CredentialsDisplayContent.java
new file mode 100644
index 0000000..106f322
--- /dev/null
+++ b/core/java/android/service/credentials/CredentialsDisplayContent.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2022 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 android.service.credentials;
+
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Content to be displayed on the account selector UI, including credential entries,
+ * actions etc.
+ *
+ * @hide
+ */
+public final class CredentialsDisplayContent implements Parcelable {
+    /** Header to be displayed on the UI. */
+    private final @Nullable CharSequence mHeader;
+
+    /** List of credential entries to be displayed on the UI. */
+    private final @NonNull List<CredentialEntry> mCredentialEntries;
+
+    /** List of provider actions to be displayed on the UI. */
+    private final @NonNull List<Action> mActions;
+
+    private CredentialsDisplayContent(@Nullable CharSequence header,
+            @NonNull List<CredentialEntry> credentialEntries,
+            @NonNull List<Action> actions) {
+        mHeader = header;
+        mCredentialEntries = credentialEntries;
+        mActions = actions;
+    }
+
+    private CredentialsDisplayContent(@NonNull Parcel in) {
+        mHeader = in.readCharSequence();
+        mCredentialEntries = in.createTypedArrayList(CredentialEntry.CREATOR);
+        mActions = in.createTypedArrayList(Action.CREATOR);
+    }
+
+    public static final @NonNull Creator<CredentialsDisplayContent> CREATOR =
+            new Creator<CredentialsDisplayContent>() {
+                @Override
+                public CredentialsDisplayContent createFromParcel(@NonNull Parcel in) {
+                    return new CredentialsDisplayContent(in);
+                }
+
+                @Override
+                public CredentialsDisplayContent[] newArray(int size) {
+                    return new CredentialsDisplayContent[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeCharSequence(mHeader);
+        dest.writeTypedList(mCredentialEntries);
+        dest.writeTypedList(mActions);
+    }
+
+    /**
+     * Returns the header to be displayed on the UI.
+     */
+    public @Nullable CharSequence getHeader() {
+        return mHeader;
+    }
+
+    /**
+     * Returns the list of credential entries to be displayed on the UI.
+     */
+    public @NonNull List<CredentialEntry> getCredentialEntries() {
+        return mCredentialEntries;
+    }
+
+    /**
+     * Returns the list of actions to be displayed on the UI.
+     */
+    public @NonNull List<Action> getActions() {
+        return mActions;
+    }
+
+    /**
+     * Builds an instance of {@link CredentialsDisplayContent}.
+     */
+    public static final class Builder {
+        private CharSequence mHeader = null;
+        private List<CredentialEntry> mCredentialEntries = new ArrayList<>();
+        private List<Action> mActions = new ArrayList<>();
+
+        /**
+         * Sets the header to be displayed on the UI.
+         */
+        public @NonNull Builder setHeader(@Nullable CharSequence header) {
+            mHeader = header;
+            return this;
+        }
+
+        /**
+         * Adds a {@link CredentialEntry} to the list of entries to be displayed on
+         * the UI.
+         *
+         * @throws NullPointerException If the {@code credentialEntry} is null.
+         */
+        public @NonNull Builder addCredentialEntry(@NonNull CredentialEntry credentialEntry) {
+            mCredentialEntries.add(Objects.requireNonNull(credentialEntry));
+            return this;
+        }
+
+        /**
+         * Adds an {@link Action} to the list of actions to be displayed on
+         * the UI.
+         *
+         * @throws NullPointerException If {@code action} is null.
+         */
+        public @NonNull Builder addAction(@NonNull Action action) {
+            mActions.add(Objects.requireNonNull(action, "action must not be null"));
+            return this;
+        }
+
+        /**
+         * Sets the list of actions to be displayed on the UI.
+         *
+         * @throws NullPointerException If {@code actions} is null, or any of its elements
+         * is null.
+         */
+        public @NonNull Builder setActions(@NonNull List<Action> actions) {
+            mActions = Preconditions.checkCollectionElementsNotNull(actions,
+                    "actions");
+            return this;
+        }
+
+        /**
+         * Sets the list of credential entries to be displayed on the
+         * account selector UI.
+         *
+         * @throws NullPointerException If {@code credentialEntries} is null, or any of its
+         * elements is null.
+         */
+        public @NonNull Builder setCredentialEntries(
+                @NonNull List<CredentialEntry> credentialEntries) {
+            mCredentialEntries = Preconditions.checkCollectionElementsNotNull(
+                    credentialEntries,
+                    "credentialEntries");
+            return this;
+        }
+
+        /**
+         * Builds a {@link GetCredentialsResponse} instance.
+         *
+         * @throws NullPointerException If {@code credentialEntries} is null.
+         * @throws IllegalStateException if both {@code credentialEntries} and
+         * {@code actions} are empty.
+         */
+        public @NonNull CredentialsDisplayContent build() {
+            if (mCredentialEntries != null && mCredentialEntries.isEmpty()
+                    && mActions != null && mActions.isEmpty()) {
+                throw new IllegalStateException("credentialEntries and actions must not both "
+                        + "be empty");
+            }
+            return new CredentialsDisplayContent(mHeader, mCredentialEntries, mActions);
+        }
+    }
+}
diff --git a/core/java/android/service/credentials/GetCredentialOption.java b/core/java/android/service/credentials/GetCredentialOption.java
new file mode 100644
index 0000000..c6cda1d
--- /dev/null
+++ b/core/java/android/service/credentials/GetCredentialOption.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2022 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 android.service.credentials;
+
+import android.annotation.NonNull;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * A type specific credential request, containing the associated data to be used for
+ * retrieving credentials.
+ *
+ * @hide
+ */
+public final class GetCredentialOption implements Parcelable {
+    /** The type of credential requested. */
+    private final @NonNull String mType;
+
+    /** The data associated with the request. */
+    private final @NonNull Bundle mData;
+
+    /**
+     * Constructs a new instance of {@link GetCredentialOption}
+     *
+     * @throws IllegalArgumentException If {@code type} string is null or empty.
+     * @throws NullPointerException If {@code data} is null.
+     */
+    public GetCredentialOption(@NonNull String type, @NonNull Bundle data) {
+        Preconditions.checkStringNotEmpty(type, "type must not be null, or empty");
+        requireNonNull(data, "data must not be null");
+        mType = type;
+        mData = data;
+    }
+
+    /**
+     * Returns the data associated with this credential request option.
+     */
+    public @NonNull Bundle getData() {
+        return mData;
+    }
+
+    /**
+     * Returns the type associated with this credential request option.
+     */
+    public @NonNull String getType() {
+        return mType;
+    }
+
+    private GetCredentialOption(@NonNull Parcel in) {
+        mType = in.readString16NoHelper();
+        mData = in.readBundle();
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString16NoHelper(mType);
+        dest.writeBundle(mData);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final @NonNull Creator<GetCredentialOption> CREATOR =
+            new Creator<GetCredentialOption>() {
+        @Override
+        public GetCredentialOption createFromParcel(@NonNull Parcel in) {
+            return new GetCredentialOption(in);
+        }
+
+        @Override
+        public GetCredentialOption[] newArray(int size) {
+            return new GetCredentialOption[size];
+        }
+    };
+}
diff --git a/core/java/android/service/credentials/GetCredentialsCallback.java b/core/java/android/service/credentials/GetCredentialsCallback.java
new file mode 100644
index 0000000..42a7394
--- /dev/null
+++ b/core/java/android/service/credentials/GetCredentialsCallback.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2022 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 android.service.credentials;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * Callback to be invoked as a response to {@link GetCredentialsRequest}.
+ *
+ * @hide
+ */
+public final class GetCredentialsCallback {
+
+    private static final String TAG = "GetCredentialsCallback";
+
+    private final IGetCredentialsCallback mCallback;
+
+    /** @hide */
+    public GetCredentialsCallback(@NonNull IGetCredentialsCallback callback) {
+        mCallback = callback;
+    }
+
+    /**
+     * Invoked on a successful response for {@link GetCredentialsRequest}
+     * @param response The response from the credential provider.
+     */
+    public void onSuccess(@NonNull GetCredentialsResponse response) {
+        try {
+            mCallback.onSuccess(response);
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Invoked on a failure response for {@link GetCredentialsRequest}
+     * @param errorCode The code defining the kind of error.
+     * @param message The message corresponding to the failure.
+     */
+    public void onFailure(int errorCode, @Nullable CharSequence message) {
+        Log.w(TAG, "onFailure: " + message);
+        try {
+            mCallback.onFailure(errorCode, message);
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
+        }
+    }
+}
diff --git a/core/java/android/service/credentials/GetCredentialsRequest.aidl b/core/java/android/service/credentials/GetCredentialsRequest.aidl
new file mode 100644
index 0000000..b309d69
--- /dev/null
+++ b/core/java/android/service/credentials/GetCredentialsRequest.aidl
@@ -0,0 +1,3 @@
+package android.service.credentials;
+
+parcelable GetCredentialsRequest;
\ No newline at end of file
diff --git a/core/java/android/service/credentials/GetCredentialsRequest.java b/core/java/android/service/credentials/GetCredentialsRequest.java
new file mode 100644
index 0000000..cf7c283
--- /dev/null
+++ b/core/java/android/service/credentials/GetCredentialsRequest.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2022 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 android.service.credentials;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Request for getting user's credentials from a given credential provider.
+ *
+ * @hide
+ */
+public final class GetCredentialsRequest implements Parcelable {
+    /** Calling package of the app requesting for credentials. */
+    private final @NonNull String mCallingPackage;
+
+    /**
+     * List of credential options. Each {@link GetCredentialOption} object holds parameters to
+     * be used for retrieving specific type of credentials.
+     */
+    private final @NonNull List<GetCredentialOption> mGetCredentialOptions;
+
+    private GetCredentialsRequest(@NonNull String callingPackage,
+            @NonNull List<GetCredentialOption> getCredentialOptions) {
+        this.mCallingPackage = callingPackage;
+        this.mGetCredentialOptions = getCredentialOptions;
+    }
+
+    private GetCredentialsRequest(@NonNull Parcel in) {
+        mCallingPackage = in.readString16NoHelper();
+        mGetCredentialOptions = in.createTypedArrayList(GetCredentialOption.CREATOR);
+    }
+
+    public static final @NonNull Creator<GetCredentialsRequest> CREATOR =
+            new Creator<GetCredentialsRequest>() {
+                @Override
+                public GetCredentialsRequest createFromParcel(Parcel in) {
+                    return new GetCredentialsRequest(in);
+                }
+
+                @Override
+                public GetCredentialsRequest[] newArray(int size) {
+                    return new GetCredentialsRequest[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString16NoHelper(mCallingPackage);
+        dest.writeTypedList(mGetCredentialOptions);
+    }
+
+    /**
+     * Returns the calling package of the app requesting credentials.
+     */
+    public @NonNull String getCallingPackage() {
+        return mCallingPackage;
+    }
+
+    /**
+     * Returns the list of type specific credential options to return credentials for.
+     */
+    public @NonNull List<GetCredentialOption> getGetCredentialOptions() {
+        return mGetCredentialOptions;
+    }
+
+    /**
+     * Builder for {@link GetCredentialsRequest}.
+     */
+    public static final class Builder {
+        private String mCallingPackage;
+        private List<GetCredentialOption> mGetCredentialOptions = new ArrayList<>();
+
+        /**
+         * Creates a new builder.
+         * @param callingPackage The calling package of the app requesting credentials.
+         *
+         * @throws IllegalArgumentException If {@code callingPackag}e is null or empty.
+         */
+        public Builder(@NonNull String callingPackage) {
+            mCallingPackage = Preconditions.checkStringNotEmpty(callingPackage);
+        }
+
+        /**
+         * Sets the list of credential options.
+         *
+         * @throws NullPointerException If {@code getCredentialOptions} itself or any of its
+         * elements is null.
+         * @throws IllegalArgumentException If {@code getCredentialOptions} is empty.
+         */
+        public @NonNull Builder setGetCredentialOptions(
+                @NonNull List<GetCredentialOption> getCredentialOptions) {
+            Preconditions.checkCollectionNotEmpty(mGetCredentialOptions,
+                    "getCredentialOptions");
+            Preconditions.checkCollectionElementsNotNull(mGetCredentialOptions,
+                    "getCredentialOptions");
+            mGetCredentialOptions = getCredentialOptions;
+            return this;
+        }
+
+        /**
+         * Adds a single {@link GetCredentialOption} object to the list of credential options.
+         *
+         * @throws NullPointerException If {@code getCredentialOption} is null.
+         */
+        public @NonNull Builder addGetCredentialOption(
+                @NonNull GetCredentialOption getCredentialOption) {
+            Objects.requireNonNull(getCredentialOption,
+                    "getCredentialOption must not be null");
+            mGetCredentialOptions.add(getCredentialOption);
+            return this;
+        }
+
+        /**
+         * Builds a new {@link GetCredentialsRequest} instance.
+         *
+         * @throws NullPointerException If {@code getCredentialOptions} is null.
+         * @throws IllegalArgumentException If {@code getCredentialOptions} is empty, or if
+         * {@code callingPackage} is null or empty.
+         */
+        public @NonNull GetCredentialsRequest build() {
+            Preconditions.checkStringNotEmpty(mCallingPackage,
+                    "Must set the calling package");
+            Preconditions.checkCollectionNotEmpty(mGetCredentialOptions,
+                    "getCredentialOptions");
+            return new GetCredentialsRequest(mCallingPackage, mGetCredentialOptions);
+        }
+    }
+}
diff --git a/core/java/android/service/credentials/GetCredentialsResponse.aidl b/core/java/android/service/credentials/GetCredentialsResponse.aidl
new file mode 100644
index 0000000..0d8c635
--- /dev/null
+++ b/core/java/android/service/credentials/GetCredentialsResponse.aidl
@@ -0,0 +1,3 @@
+package android.service.credentials;
+
+parcelable GetCredentialsResponse;
\ No newline at end of file
diff --git a/core/java/android/service/credentials/GetCredentialsResponse.java b/core/java/android/service/credentials/GetCredentialsResponse.java
new file mode 100644
index 0000000..293867b
--- /dev/null
+++ b/core/java/android/service/credentials/GetCredentialsResponse.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2022 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 android.service.credentials;
+
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import java.util.Objects;
+
+/**
+ * Response from a credential provider, containing credential entries and other associated
+ * data to be shown on the account selector UI.
+ *
+ * @hide
+ */
+public final class GetCredentialsResponse implements Parcelable {
+    /** Content to be used for the UI. */
+    private final @Nullable CredentialsDisplayContent mCredentialsDisplayContent;
+
+    /**
+     * Authentication action that must be launched and completed before showing any content
+     * from the provider.
+     */
+    private final @Nullable Action mAuthenticationAction;
+
+    /**
+     * Creates a {@link GetCredentialsRequest} instance with an authentication action set.
+     * Providers must use this method when no content can be shown before authentication.
+     *
+     * @throws NullPointerException If {@code authenticationAction} is null.
+     */
+    public static @NonNull GetCredentialsResponse createWithAuthentication(
+            @NonNull Action authenticationAction) {
+        Objects.requireNonNull(authenticationAction,
+                "authenticationAction must not be null");
+        return new GetCredentialsResponse(null, authenticationAction);
+    }
+
+    /**
+     * Creates a {@link GetCredentialsRequest} instance with display content to be shown on the UI.
+     * Providers must use this method when there is content to be shown without top level
+     * authentication required.
+     *
+     * @throws NullPointerException If {@code credentialsDisplayContent} is null.
+     */
+    public static @NonNull GetCredentialsResponse createWithDisplayContent(
+            @NonNull CredentialsDisplayContent credentialsDisplayContent) {
+        Objects.requireNonNull(credentialsDisplayContent,
+                "credentialsDisplayContent must not be null");
+        return new GetCredentialsResponse(credentialsDisplayContent, null);
+    }
+
+    private GetCredentialsResponse(@Nullable CredentialsDisplayContent credentialsDisplayContent,
+            @Nullable Action authenticationAction) {
+        mCredentialsDisplayContent = credentialsDisplayContent;
+        mAuthenticationAction = authenticationAction;
+    }
+
+    private GetCredentialsResponse(@NonNull Parcel in) {
+        mCredentialsDisplayContent = in.readParcelable(CredentialsDisplayContent.class
+                .getClassLoader(), CredentialsDisplayContent.class);
+        mAuthenticationAction = in.readParcelable(Action.class.getClassLoader(), Action.class);
+    }
+
+    public static final @NonNull Creator<GetCredentialsResponse> CREATOR =
+            new Creator<GetCredentialsResponse>() {
+                @Override
+                public GetCredentialsResponse createFromParcel(Parcel in) {
+                    return new GetCredentialsResponse(in);
+                }
+
+                @Override
+                public GetCredentialsResponse[] newArray(int size) {
+                    return new GetCredentialsResponse[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeParcelable(mCredentialsDisplayContent, flags);
+        dest.writeParcelable(mAuthenticationAction, flags);
+    }
+
+    /**
+     * Returns whether the response contains a top level authentication action.
+     */
+    public @NonNull boolean isAuthenticationActionSet() {
+        return mAuthenticationAction != null;
+    }
+
+    /**
+     * Returns the authentication action to be invoked before any other content
+     * can be shown to the user.
+     */
+    public @NonNull Action getAuthenticationAction() {
+        return mAuthenticationAction;
+    }
+
+    /**
+     * Returns the credentialDisplayContent that does not require authentication, and
+     * can be shown to the user on the account selector UI.
+     */
+    public @NonNull CredentialsDisplayContent getCredentialsDisplayContent() {
+        return mCredentialsDisplayContent;
+    }
+}
diff --git a/core/java/android/service/credentials/ICreateCredentialCallback.aidl b/core/java/android/service/credentials/ICreateCredentialCallback.aidl
new file mode 100644
index 0000000..4cc76a4
--- /dev/null
+++ b/core/java/android/service/credentials/ICreateCredentialCallback.aidl
@@ -0,0 +1,13 @@
+package android.service.credentials;
+
+import android.service.credentials.CreateCredentialResponse;
+
+/**
+ * Interface from the system to a credential provider service.
+ *
+ * @hide
+ */
+oneway interface ICreateCredentialCallback {
+    void onSuccess(in CreateCredentialResponse request);
+    void onFailure(int errorCode, in CharSequence message);
+}
\ No newline at end of file
diff --git a/core/java/android/service/credentials/ICredentialProviderService.aidl b/core/java/android/service/credentials/ICredentialProviderService.aidl
new file mode 100644
index 0000000..c68430c
--- /dev/null
+++ b/core/java/android/service/credentials/ICredentialProviderService.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2016 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 android.service.credentials;
+
+import android.os.ICancellationSignal;
+import android.service.credentials.GetCredentialsRequest;
+import android.service.credentials.CreateCredentialRequest;
+import android.service.credentials.IGetCredentialsCallback;
+import android.service.credentials.ICreateCredentialCallback;
+
+/**
+ * Interface from the system to a credential provider service.
+ *
+ * @hide
+ */
+oneway interface ICredentialProviderService {
+    void onGetCredentials(in GetCredentialsRequest request, in ICancellationSignal transport, in IGetCredentialsCallback callback);
+    void onCreateCredential(in CreateCredentialRequest request, in ICancellationSignal transport, in ICreateCredentialCallback callback);
+}
diff --git a/core/java/android/service/credentials/IGetCredentialsCallback.aidl b/core/java/android/service/credentials/IGetCredentialsCallback.aidl
new file mode 100644
index 0000000..6e20c55
--- /dev/null
+++ b/core/java/android/service/credentials/IGetCredentialsCallback.aidl
@@ -0,0 +1,13 @@
+package android.service.credentials;
+
+import android.service.credentials.GetCredentialsResponse;
+
+/**
+ * Interface from the system to a credential provider service.
+ *
+ * @hide
+ */
+oneway interface IGetCredentialsCallback {
+    void onSuccess(in GetCredentialsResponse response);
+    void onFailure(int errorCode, in CharSequence message);
+}
\ No newline at end of file
diff --git a/core/java/android/service/credentials/SaveEntry.java b/core/java/android/service/credentials/SaveEntry.java
new file mode 100644
index 0000000..28fec30
--- /dev/null
+++ b/core/java/android/service/credentials/SaveEntry.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2022 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 android.service.credentials;
+
+import android.annotation.Nullable;
+import android.app.PendingIntent;
+import android.app.slice.Slice;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+
+/**
+ * An entry to be shown on the UI. This entry represents where the credential to be created will
+ * be stored. Examples include user's account, family group etc.
+ *
+ * @hide
+ */
+public final class SaveEntry implements Parcelable {
+    private final @NonNull Slice mInfo;
+    private final @Nullable PendingIntent mPendingIntent;
+    private final @Nullable Credential mCredential;
+
+    private SaveEntry(@NonNull Parcel in) {
+        mInfo = in.readParcelable(Slice.class.getClassLoader(), Slice.class);
+        mPendingIntent = in.readParcelable(PendingIntent.class.getClassLoader(),
+                PendingIntent.class);
+        mCredential = in.readParcelable(Credential.class.getClassLoader(), Credential.class);
+    }
+
+    public static final @NonNull Creator<SaveEntry> CREATOR = new Creator<SaveEntry>() {
+        @Override
+        public SaveEntry createFromParcel(@NonNull Parcel in) {
+            return new SaveEntry(in);
+        }
+
+        @Override
+        public SaveEntry[] newArray(int size) {
+            return new SaveEntry[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        mInfo.writeToParcel(dest, flags);
+        mPendingIntent.writeToParcel(dest, flags);
+        mCredential.writeToParcel(dest, flags);
+    }
+
+    /* package-private */ SaveEntry(
+            @NonNull Slice info,
+            @Nullable PendingIntent pendingIntent,
+            @Nullable Credential credential) {
+        this.mInfo = info;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mInfo);
+        this.mPendingIntent = pendingIntent;
+        this.mCredential = credential;
+    }
+
+    /** Returns the info to be displayed with this save entry on the UI. */
+    public @NonNull Slice getInfo() {
+        return mInfo;
+    }
+
+    /** Returns the pendingIntent to be invoked when this save entry on the UI is selectcd. */
+    public @Nullable PendingIntent getPendingIntent() {
+        return mPendingIntent;
+    }
+
+    /** Returns the credential produced by the {@link CreateCredentialRequest}. */
+    public @Nullable Credential getCredential() {
+        return mCredential;
+    }
+
+    /**
+     * A builder for {@link SaveEntry}.
+     */
+    public static final class Builder {
+
+        private @NonNull Slice mInfo;
+        private @Nullable PendingIntent mPendingIntent;
+        private @Nullable Credential mCredential;
+
+        /**
+         * Builds the instance.
+         * @param info The info to be displayed with this save entry.
+         *
+         * @throws NullPointerException If {@code info} is null.
+         */
+        public Builder(@NonNull Slice info) {
+            mInfo = Objects.requireNonNull(info, "info must not be null");
+        }
+
+        /**
+         * Sets the pendingIntent to be invoked when this entry is selected by the user.
+         *
+         * @throws IllegalStateException If {@code credential} is already set. Must only set either
+         * {@code credential}, or the {@code pendingIntent}.
+         */
+        public @NonNull Builder setPendingIntent(@Nullable PendingIntent pendingIntent) {
+            Preconditions.checkState(pendingIntent != null
+                    && mCredential != null, "credential is already set. Must only set "
+                    + "either the pendingIntent or the credential");
+            mPendingIntent = pendingIntent;
+            return this;
+        }
+
+        /**
+         * Sets the credential to be returned when this entry is selected by the user.
+         *
+         * @throws IllegalStateException If {@code pendingIntent} is already set. Must only
+         * set either the {@code pendingIntent}, or {@code credential}.
+         */
+        public @NonNull Builder setCredential(@Nullable Credential credential) {
+            Preconditions.checkState(credential != null && mPendingIntent != null,
+                    "pendingIntent is already set. Must only set either the credential "
+                            + "or the pendingIntent");
+            mCredential = credential;
+            return this;
+        }
+
+        /**
+         * Builds the instance.
+         *
+         * @throws IllegalStateException if both {@code pendingIntent} and {@code credential}
+         * are null.
+         */
+        public @NonNull SaveEntry build() {
+            Preconditions.checkState(mPendingIntent == null && mCredential == null,
+                    "pendingIntent and credential both must not be null. Must set "
+                            + "either the pendingIntnet or the credential");
+            return new SaveEntry(
+                    mInfo,
+                    mPendingIntent,
+                    mCredential);
+        }
+    }
+}
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index 1b46107..1285d1e 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -345,6 +345,12 @@
      * Calling this a second time invalidates the previously created hotword detector
      * which can no longer be used to manage recognition.
      *
+     * <p>Note: If there are any active detectors that are created by using
+     * {@link #createAlwaysOnHotwordDetector(String, Locale, PersistableBundle, SharedMemory,
+     * AlwaysOnHotwordDetector.Callback)} or {@link #createHotwordDetector(PersistableBundle,
+     * SharedMemory, HotwordDetector.Callback)}, call this will throw an
+     * {@link IllegalArgumentException}.
+     *
      * @param keyphrase The keyphrase that's being used, for example "Hello Android".
      * @param locale The locale for which the enrollment needs to be performed.
      * @param callback The callback to notify of detection events.
@@ -377,6 +383,10 @@
      * <p>Note: The system will trigger hotword detection service after calling this function when
      * all conditions meet the requirements.
      *
+     * <p>Note: If there are any active detectors that are created by using
+     * {@link #createAlwaysOnHotwordDetector(String, Locale, AlwaysOnHotwordDetector.Callback)},
+     * call this will throw an {@link IllegalArgumentException}.
+     *
      * @param keyphrase The keyphrase that's being used, for example "Hello Android".
      * @param locale The locale for which the enrollment needs to be performed.
      * @param options Application configuration data provided by the
@@ -420,6 +430,14 @@
                 safelyShutdownAllHotwordDetectors();
             }
 
+            for (HotwordDetector detector : mActiveHotwordDetectors) {
+                if (detector.isUsingHotwordDetectionService() != supportHotwordDetectionService) {
+                    throw new IllegalArgumentException(
+                            "It disallows to create trusted and non-trusted detectors "
+                                    + "at the same time.");
+                }
+            }
+
             AlwaysOnHotwordDetector dspDetector = new AlwaysOnHotwordDetector(keyphrase, locale,
                     callback, mKeyphraseEnrollmentInfo, mSystemService,
                     getApplicationContext().getApplicationInfo().targetSdkVersion,
@@ -460,6 +478,10 @@
      * devices where hardware filtering is available (such as through a DSP), it's highly
      * recommended to use {@link #createAlwaysOnHotwordDetector} instead.
      *
+     * <p>Note: If there are any active detectors that are created by using
+     * {@link #createAlwaysOnHotwordDetector(String, Locale, AlwaysOnHotwordDetector.Callback)},
+     * call this will throw an {@link IllegalArgumentException}.
+     *
      * @param options Application configuration data to be provided to the
      * {@link HotwordDetectionService}. PersistableBundle does not allow any remotable objects or
      * other contents that can be used to communicate with other processes.
@@ -490,7 +512,11 @@
                 safelyShutdownAllHotwordDetectors();
             } else {
                 for (HotwordDetector detector : mActiveHotwordDetectors) {
-                    if (detector instanceof SoftwareHotwordDetector) {
+                    if (!detector.isUsingHotwordDetectionService()) {
+                        throw new IllegalArgumentException(
+                                "It disallows to create trusted and non-trusted detectors "
+                                        + "at the same time.");
+                    } else if (detector instanceof SoftwareHotwordDetector) {
                         throw new IllegalArgumentException(
                                 "There is already an active SoftwareHotwordDetector. "
                                         + "It must be destroyed to create a new one.");
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index b2a26fa..067946e 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -67,6 +67,7 @@
 import android.view.displayhash.DisplayHash;
 import android.view.displayhash.VerifiedDisplayHash;
 import android.window.ITaskFpsCallback;
+import android.window.ScreenCapture;
 
 /**
  * System private interface to the window manager.
@@ -968,4 +969,11 @@
      * treatment.
      */
     boolean isLetterboxBackgroundMultiColored();
+
+    /**
+     * Captures the entire display specified by the displayId using the args provided. If the args
+     * are null or if the sourceCrop is invalid or null, the entire display bounds will be captured.
+     */
+    oneway void captureDisplay(int displayId, in @nullable ScreenCapture.CaptureArgs captureArgs,
+            in ScreenCapture.ScreenCaptureListener listener);
 }
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index c97eb73..d77e882 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -196,8 +196,6 @@
      */
     public static boolean sRendererEnabled = true;
 
-    public static boolean sTrimForeground = false;
-
     /**
      * Controls whether or not the renderer should aggressively trim
      * memory. Note that this must not be set for any process that uses
@@ -205,9 +203,10 @@
      * that do not go into the background.
      */
     public static void enableForegroundTrimming() {
-        sTrimForeground = true;
+        // TODO: Remove
     }
 
+
     /**
      * Initialize HWUI for being in a system process like system_server
      * Should not be called in non-system processes
@@ -218,9 +217,8 @@
         // process.
         if (!ActivityManager.isHighEndGfx()) {
             sRendererEnabled = false;
-        } else {
-            enableForegroundTrimming();
         }
+        setIsSystemOrPersistent();
     }
 
     /**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index b6f775d..28fa77c 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1766,9 +1766,6 @@
                 mAppVisibilityChanged = true;
                 scheduleTraversals();
             }
-            if (!mAppVisible) {
-                WindowManagerGlobal.trimForeground();
-            }
             AnimationHandler.requestAnimatorsEnabled(mAppVisible, this);
         }
     }
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index d377565..4a9dc5b 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -19,9 +19,7 @@
 import android.animation.ValueAnimator;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.ActivityManager;
 import android.compat.annotation.UnsupportedAppUsage;
-import android.content.ComponentCallbacks2;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.res.Configuration;
@@ -524,9 +522,6 @@
             }
             allViewsRemoved = mRoots.isEmpty();
         }
-        if (ThreadedRenderer.sTrimForeground) {
-            doTrimForeground();
-        }
 
         // If we don't have any views anymore in our process, we no longer need the
         // InsetsAnimationThread to save some resources.
@@ -543,65 +538,9 @@
         return index;
     }
 
-    public static boolean shouldDestroyEglContext(int trimLevel) {
-        // On low-end gfx devices we trim when memory is moderate;
-        // on high-end devices we do this when low.
-        if (trimLevel >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE) {
-            return true;
-        }
-        if (trimLevel >= ComponentCallbacks2.TRIM_MEMORY_MODERATE
-                && !ActivityManager.isHighEndGfx()) {
-            return true;
-        }
-        return false;
-    }
-
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public void trimMemory(int level) {
-
-        if (shouldDestroyEglContext(level)) {
-            // Destroy all hardware surfaces and resources associated to
-            // known windows
-            synchronized (mLock) {
-                for (int i = mRoots.size() - 1; i >= 0; --i) {
-                    mRoots.get(i).destroyHardwareResources();
-                }
-            }
-            // Force a full memory flush
-            level = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
-        }
-
         ThreadedRenderer.trimMemory(level);
-
-        if (ThreadedRenderer.sTrimForeground) {
-            doTrimForeground();
-        }
-    }
-
-    public static void trimForeground() {
-        if (ThreadedRenderer.sTrimForeground) {
-            WindowManagerGlobal wm = WindowManagerGlobal.getInstance();
-            wm.doTrimForeground();
-        }
-    }
-
-    private void doTrimForeground() {
-        boolean hasVisibleWindows = false;
-        synchronized (mLock) {
-            for (int i = mRoots.size() - 1; i >= 0; --i) {
-                final ViewRootImpl root = mRoots.get(i);
-                if (root.mView != null && root.getHostVisibility() == View.VISIBLE
-                        && root.mAttachInfo.mThreadedRenderer != null) {
-                    hasVisibleWindows = true;
-                } else {
-                    root.destroyHardwareResources();
-                }
-            }
-        }
-        if (!hasVisibleWindows) {
-            ThreadedRenderer.trimMemory(
-                    ComponentCallbacks2.TRIM_MEMORY_COMPLETE);
-        }
     }
 
     public void dumpGfxInfo(FileDescriptor fd, String[] args) {
diff --git a/core/java/android/window/ScreenCapture.aidl b/core/java/android/window/ScreenCapture.aidl
new file mode 100644
index 0000000..267a7c6
--- /dev/null
+++ b/core/java/android/window/ScreenCapture.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2022 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 android.window;
+
+/** @hide */
+parcelable ScreenCapture.CaptureArgs;
+
+/** @hide */
+parcelable ScreenCapture.ScreenshotHardwareBuffer;
+
+/** @hide */
+parcelable ScreenCapture.ScreenCaptureListener;
\ No newline at end of file
diff --git a/core/java/android/window/ScreenCapture.java b/core/java/android/window/ScreenCapture.java
index 887d027..8a7efb9 100644
--- a/core/java/android/window/ScreenCapture.java
+++ b/core/java/android/window/ScreenCapture.java
@@ -24,11 +24,17 @@
 import android.graphics.Rect;
 import android.hardware.HardwareBuffer;
 import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.util.Log;
+import android.util.Pair;
 import android.view.SurfaceControl;
 
+import libcore.util.NativeAllocationRegistry;
+
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
 
 /**
  * Handles display and layer captures for the system.
@@ -37,20 +43,26 @@
  */
 public class ScreenCapture {
     private static final String TAG = "ScreenCapture";
+    private static final int SCREENSHOT_WAIT_TIME_S = 1;
 
     private static native int nativeCaptureDisplay(DisplayCaptureArgs captureArgs,
-            ScreenCaptureListener captureListener);
+            long captureListener);
     private static native int nativeCaptureLayers(LayerCaptureArgs captureArgs,
-            ScreenCaptureListener captureListener);
+            long captureListener);
+    private static native long nativeCreateScreenCaptureListener(
+            Consumer<ScreenshotHardwareBuffer> consumer);
+    private static native void nativeWriteListenerToParcel(long nativeObject, Parcel out);
+    private static native long nativeReadListenerFromParcel(Parcel in);
+    private static native long getNativeListenerFinalizer();
 
     /**
-     * @param captureArgs Arguments about how to take the screenshot
+     * @param captureArgs     Arguments about how to take the screenshot
      * @param captureListener A listener to receive the screenshot callback
      * @hide
      */
     public static int captureDisplay(@NonNull DisplayCaptureArgs captureArgs,
             @NonNull ScreenCaptureListener captureListener) {
-        return nativeCaptureDisplay(captureArgs, captureListener);
+        return nativeCaptureDisplay(captureArgs, captureListener.mNativeObject);
     }
 
     /**
@@ -61,28 +73,29 @@
      */
     public static ScreenshotHardwareBuffer captureDisplay(
             DisplayCaptureArgs captureArgs) {
-        SyncScreenCaptureListener
-                screenCaptureListener = new SyncScreenCaptureListener();
-
-        int status = captureDisplay(captureArgs, screenCaptureListener);
+        Pair<ScreenCaptureListener, ScreenshotSync> syncScreenCapture = createSyncCaptureListener();
+        int status = captureDisplay(captureArgs, syncScreenCapture.first);
         if (status != 0) {
             return null;
         }
 
-        return screenCaptureListener.waitForScreenshot();
+        try {
+            return syncScreenCapture.second.get();
+        } catch (Exception e) {
+            return null;
+        }
     }
 
     /**
      * Captures a layer and its children and returns a {@link HardwareBuffer} with the content.
      *
-     * @param layer            The root layer to capture.
-     * @param sourceCrop       The portion of the root surface to capture; caller may pass in 'new
-     *                         Rect()' or null if no cropping is desired. If the root layer does not
-     *                         have a buffer or a crop set, then a non-empty source crop must be
-     *                         specified.
-     * @param frameScale       The desired scale of the returned buffer; the raw
-     *                         screen will be scaled up/down.
-     *
+     * @param layer      The root layer to capture.
+     * @param sourceCrop The portion of the root surface to capture; caller may pass in 'new
+     *                   Rect()' or null if no cropping is desired. If the root layer does not
+     *                   have a buffer or a crop set, then a non-empty source crop must be
+     *                   specified.
+     * @param frameScale The desired scale of the returned buffer; the raw screen will be scaled
+     *                   up/down.
      * @return Returns a HardwareBuffer that contains the layer capture.
      * @hide
      */
@@ -94,15 +107,14 @@
     /**
      * Captures a layer and its children and returns a {@link HardwareBuffer} with the content.
      *
-     * @param layer            The root layer to capture.
-     * @param sourceCrop       The portion of the root surface to capture; caller may pass in 'new
-     *                         Rect()' or null if no cropping is desired. If the root layer does not
-     *                         have a buffer or a crop set, then a non-empty source crop must be
-     *                         specified.
-     * @param frameScale       The desired scale of the returned buffer; the raw
-     *                         screen will be scaled up/down.
-     * @param format           The desired pixel format of the returned buffer.
-     *
+     * @param layer      The root layer to capture.
+     * @param sourceCrop The portion of the root surface to capture; caller may pass in 'new
+     *                   Rect()' or null if no cropping is desired. If the root layer does not
+     *                   have a buffer or a crop set, then a non-empty source crop must be
+     *                   specified.
+     * @param frameScale The desired scale of the returned buffer; the raw screen will be scaled
+     *                   up/down.
+     * @param format     The desired pixel format of the returned buffer.
      * @return Returns a HardwareBuffer that contains the layer capture.
      * @hide
      */
@@ -120,21 +132,24 @@
     /**
      * @hide
      */
-    public static ScreenshotHardwareBuffer captureLayers(
-            LayerCaptureArgs captureArgs) {
-        SyncScreenCaptureListener screenCaptureListener = new SyncScreenCaptureListener();
-
-        int status = captureLayers(captureArgs, screenCaptureListener);
+    public static ScreenshotHardwareBuffer captureLayers(LayerCaptureArgs captureArgs) {
+        Pair<ScreenCaptureListener, ScreenshotSync> syncScreenCapture = createSyncCaptureListener();
+        int status = captureLayers(captureArgs, syncScreenCapture.first);
         if (status != 0) {
             return null;
         }
 
-        return screenCaptureListener.waitForScreenshot();
+        try {
+            return syncScreenCapture.second.get();
+        } catch (Exception e) {
+            return null;
+        }
     }
 
     /**
      * Like {@link #captureLayers(SurfaceControl, Rect, float, int)} but with an array of layer
      * handles to exclude.
+     *
      * @hide
      */
     public static ScreenshotHardwareBuffer captureLayersExcluding(SurfaceControl layer,
@@ -150,24 +165,13 @@
     }
 
     /**
-     * @param captureArgs Arguments about how to take the screenshot
+     * @param captureArgs     Arguments about how to take the screenshot
      * @param captureListener A listener to receive the screenshot callback
      * @hide
      */
     public static int captureLayers(@NonNull LayerCaptureArgs captureArgs,
             @NonNull ScreenCaptureListener captureListener) {
-        return nativeCaptureLayers(captureArgs, captureListener);
-    }
-
-    /**
-     * @hide
-     */
-    public interface ScreenCaptureListener {
-        /**
-         * The callback invoked when the screen capture is complete.
-         * @param hardwareBuffer Data containing info about the screen capture.
-         */
-        void onScreenCaptureComplete(ScreenshotHardwareBuffer hardwareBuffer);
+        return nativeCaptureLayers(captureArgs, captureListener.mNativeObject);
     }
 
     /**
@@ -190,15 +194,16 @@
             mContainsHdrLayers = containsHdrLayers;
         }
 
-       /**
-        * Create ScreenshotHardwareBuffer from an existing HardwareBuffer object.
-        * @param hardwareBuffer The existing HardwareBuffer object
-        * @param namedColorSpace Integer value of a named color space {@link ColorSpace.Named}
-        * @param containsSecureLayers Indicates whether this graphic buffer contains captured
-        *                             contents of secure layers, in which case the screenshot
-        *                             should not be persisted.
-        * @param containsHdrLayers Indicates whether this graphic buffer contains HDR content.
-        */
+        /**
+         * Create ScreenshotHardwareBuffer from an existing HardwareBuffer object.
+         *
+         * @param hardwareBuffer       The existing HardwareBuffer object
+         * @param namedColorSpace      Integer value of a named color space {@link ColorSpace.Named}
+         * @param containsSecureLayers Indicates whether this graphic buffer contains captured
+         *                             contents of secure layers, in which case the screenshot
+         *                             should not be persisted.
+         * @param containsHdrLayers    Indicates whether this graphic buffer contains HDR content.
+         */
         private static ScreenshotHardwareBuffer createFromNative(HardwareBuffer hardwareBuffer,
                 int namedColorSpace, boolean containsSecureLayers, boolean containsHdrLayers) {
             ColorSpace colorSpace = ColorSpace.get(ColorSpace.Named.values()[namedColorSpace]);
@@ -220,6 +225,7 @@
         public boolean containsSecureLayers() {
             return mContainsSecureLayers;
         }
+
         /**
          * Returns whether the screenshot contains at least one HDR layer.
          * This information may be useful for informing the display whether this screenshot
@@ -234,7 +240,7 @@
          * Note: If you want to modify the Bitmap in software, you will need to copy the Bitmap
          * into
          * a software Bitmap using {@link Bitmap#copy(Bitmap.Config, boolean)}
-         *
+         * <p>
          * CAVEAT: This can be extremely slow; avoid use unless absolutely necessary; prefer to
          * directly
          * use the {@link HardwareBuffer} directly.
@@ -250,44 +256,23 @@
         }
     }
 
-    private static class SyncScreenCaptureListener implements ScreenCaptureListener {
-        private static final int SCREENSHOT_WAIT_TIME_S = 1;
-        private ScreenshotHardwareBuffer mScreenshotHardwareBuffer;
-        private final CountDownLatch mCountDownLatch = new CountDownLatch(1);
-
-        @Override
-        public void onScreenCaptureComplete(ScreenshotHardwareBuffer hardwareBuffer) {
-            mScreenshotHardwareBuffer = hardwareBuffer;
-            mCountDownLatch.countDown();
-        }
-
-        private ScreenshotHardwareBuffer waitForScreenshot() {
-            try {
-                mCountDownLatch.await(SCREENSHOT_WAIT_TIME_S, TimeUnit.SECONDS);
-            } catch (Exception e) {
-                Log.e(TAG, "Failed to wait for screen capture result", e);
-            }
-
-            return mScreenshotHardwareBuffer;
-        }
-    }
-
     /**
      * A common arguments class used for various screenshot requests. This contains arguments that
      * are shared between {@link DisplayCaptureArgs} and {@link LayerCaptureArgs}
+     *
      * @hide
      */
-    private abstract static class CaptureArgs {
-        private final int mPixelFormat;
-        private final Rect mSourceCrop = new Rect();
-        private final float mFrameScaleX;
-        private final float mFrameScaleY;
-        private final boolean mCaptureSecureLayers;
-        private final boolean mAllowProtected;
-        private final long mUid;
-        private final boolean mGrayscale;
+    public static class CaptureArgs implements Parcelable {
+        public final int mPixelFormat;
+        public final Rect mSourceCrop = new Rect();
+        public final float mFrameScaleX;
+        public final float mFrameScaleY;
+        public final boolean mCaptureSecureLayers;
+        public final boolean mAllowProtected;
+        public final long mUid;
+        public final boolean mGrayscale;
 
-        private CaptureArgs(Builder<? extends Builder<?>> builder) {
+        private CaptureArgs(CaptureArgs.Builder<? extends CaptureArgs.Builder<?>> builder) {
             mPixelFormat = builder.mPixelFormat;
             mSourceCrop.set(builder.mSourceCrop);
             mFrameScaleX = builder.mFrameScaleX;
@@ -298,12 +283,23 @@
             mGrayscale = builder.mGrayscale;
         }
 
+        private CaptureArgs(Parcel in) {
+            mPixelFormat = in.readInt();
+            mSourceCrop.readFromParcel(in);
+            mFrameScaleX = in.readFloat();
+            mFrameScaleY = in.readFloat();
+            mCaptureSecureLayers = in.readBoolean();
+            mAllowProtected = in.readBoolean();
+            mUid = in.readLong();
+            mGrayscale = in.readBoolean();
+        }
+
         /**
          * The Builder class used to construct {@link CaptureArgs}
          *
-         * @param <T> A builder that extends {@link Builder}
+         * @param <T> A builder that extends {@link CaptureArgs.Builder}
          */
-        abstract static class Builder<T extends Builder<T>> {
+        public static class Builder<T extends CaptureArgs.Builder<T>> {
             private int mPixelFormat = PixelFormat.RGBA_8888;
             private final Rect mSourceCrop = new Rect();
             private float mFrameScaleX = 1;
@@ -314,6 +310,14 @@
             private boolean mGrayscale;
 
             /**
+             * Construct a new {@link CaptureArgs} with the set parameters. The builder remains
+             * valid.
+             */
+            public CaptureArgs build() {
+                return new CaptureArgs(this);
+            }
+
+            /**
              * The desired pixel format of the returned buffer.
              */
             public T setPixelFormat(int pixelFormat) {
@@ -395,15 +399,47 @@
             /**
              * Each sub class should return itself to allow the builder to chain properly
              */
-            abstract T getThis();
+            T getThis() {
+                return (T) this;
+            }
         }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            dest.writeInt(mPixelFormat);
+            mSourceCrop.writeToParcel(dest, flags);
+            dest.writeFloat(mFrameScaleX);
+            dest.writeFloat(mFrameScaleY);
+            dest.writeBoolean(mCaptureSecureLayers);
+            dest.writeBoolean(mAllowProtected);
+            dest.writeLong(mUid);
+            dest.writeBoolean(mGrayscale);
+        }
+
+        public static final Parcelable.Creator<CaptureArgs> CREATOR =
+                new Parcelable.Creator<CaptureArgs>() {
+                    @Override
+                    public CaptureArgs createFromParcel(Parcel in) {
+                        return new CaptureArgs(in);
+                    }
+
+                    @Override
+                    public CaptureArgs[] newArray(int size) {
+                        return new CaptureArgs[size];
+                    }
+                };
     }
 
     /**
      * The arguments class used to make display capture requests.
      *
-     * @see #nativeCaptureDisplay(DisplayCaptureArgs, ScreenCaptureListener)
      * @hide
+     * @see #nativeCaptureDisplay(DisplayCaptureArgs, long)
      */
     public static class DisplayCaptureArgs extends CaptureArgs {
         private final IBinder mDisplayToken;
@@ -488,8 +524,8 @@
     /**
      * The arguments class used to make layer capture requests.
      *
-     * @see #nativeCaptureLayers(LayerCaptureArgs, ScreenCaptureListener)
      * @hide
+     * @see #nativeCaptureLayers(LayerCaptureArgs, long)
      */
     public static class LayerCaptureArgs extends CaptureArgs {
         private final long mNativeLayer;
@@ -530,6 +566,17 @@
                 return new LayerCaptureArgs(this);
             }
 
+            public Builder(SurfaceControl layer, CaptureArgs args) {
+                setLayer(layer);
+                setPixelFormat(args.mPixelFormat);
+                setSourceCrop(args.mSourceCrop);
+                setFrameScale(args.mFrameScaleX, args.mFrameScaleY);
+                setCaptureSecureLayers(args.mCaptureSecureLayers);
+                setAllowProtected(args.mAllowProtected);
+                setUid(args.mUid);
+                setGrayscale(args.mGrayscale);
+            }
+
             public Builder(SurfaceControl layer) {
                 setLayer(layer);
             }
@@ -542,7 +589,6 @@
                 return this;
             }
 
-
             /**
              * An array of layer handles to exclude.
              */
@@ -564,8 +610,109 @@
             Builder getThis() {
                 return this;
             }
-
         }
     }
 
+    /**
+     * The object used to receive the results when invoking screen capture requests via
+     * {@link #captureDisplay(DisplayCaptureArgs, ScreenCaptureListener)} or
+     * {@link #captureLayers(LayerCaptureArgs, ScreenCaptureListener)}
+     *
+     * This listener can only be used for a single call to capture content call.
+     */
+    public static class ScreenCaptureListener implements Parcelable {
+        private final long mNativeObject;
+        private static final NativeAllocationRegistry sRegistry =
+                NativeAllocationRegistry.createMalloced(
+                        ScreenCaptureListener.class.getClassLoader(), getNativeListenerFinalizer());
+
+        /**
+         * @param consumer The callback invoked when the screen capture is complete.
+         */
+        public ScreenCaptureListener(Consumer<ScreenshotHardwareBuffer> consumer) {
+            mNativeObject = nativeCreateScreenCaptureListener(consumer);
+            sRegistry.registerNativeAllocation(this, mNativeObject);
+        }
+
+        private ScreenCaptureListener(Parcel in) {
+            if (in.readBoolean()) {
+                mNativeObject = nativeReadListenerFromParcel(in);
+                sRegistry.registerNativeAllocation(this, mNativeObject);
+            } else {
+                mNativeObject = 0;
+            }
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            if (mNativeObject == 0) {
+                dest.writeBoolean(false);
+            } else {
+                dest.writeBoolean(true);
+                nativeWriteListenerToParcel(mNativeObject, dest);
+            }
+        }
+
+        public static final Parcelable.Creator<ScreenCaptureListener> CREATOR =
+                new Parcelable.Creator<ScreenCaptureListener>() {
+                    @Override
+                    public ScreenCaptureListener createFromParcel(Parcel in) {
+                        return new ScreenCaptureListener(in);
+                    }
+
+                    @Override
+                    public ScreenCaptureListener[] newArray(int size) {
+                        return new ScreenCaptureListener[0];
+                    }
+                };
+    }
+
+    /**
+     * A helper method to handle the async screencapture callbacks synchronously. This should only
+     * be used if the screencapture caller doesn't care that it blocks waiting for a screenshot.
+     *
+     * @return a Pair that holds the {@link ScreenCaptureListener} that should be used for capture
+     * calls into SurfaceFlinger and a {@link ScreenshotSync} object to retrieve the results.
+     */
+    public static Pair<ScreenCaptureListener, ScreenshotSync> createSyncCaptureListener() {
+        final ScreenshotSync screenshotSync = new ScreenshotSync();
+        final ScreenCaptureListener screenCaptureListener = new ScreenCaptureListener(
+                screenshotSync::setScreenshotHardwareBuffer);
+        return new Pair<>(screenCaptureListener, screenshotSync);
+    }
+
+    /**
+     * Helper class to synchronously get the {@link ScreenshotHardwareBuffer} when calling
+     * {@link #captureLayers(LayerCaptureArgs, ScreenCaptureListener)} or
+     * {@link #captureDisplay(DisplayCaptureArgs, ScreenCaptureListener)}
+     */
+    public static class ScreenshotSync {
+        private final CountDownLatch mCountDownLatch = new CountDownLatch(1);
+        private ScreenshotHardwareBuffer mScreenshotHardwareBuffer;
+
+        private void setScreenshotHardwareBuffer(
+                ScreenshotHardwareBuffer screenshotHardwareBuffer) {
+            mScreenshotHardwareBuffer = screenshotHardwareBuffer;
+            mCountDownLatch.countDown();
+        }
+
+        /**
+         * Get the {@link ScreenshotHardwareBuffer} synchronously. This can be null if the
+         * screenshot failed or if there was no callback in {@link #SCREENSHOT_WAIT_TIME_S} seconds.
+         */
+        public ScreenshotHardwareBuffer get() {
+            try {
+                mCountDownLatch.await(SCREENSHOT_WAIT_TIME_S, TimeUnit.SECONDS);
+                return mScreenshotHardwareBuffer;
+            } catch (Exception e) {
+                Log.e(TAG, "Failed to wait for screen capture result", e);
+                return null;
+            }
+        }
+    }
 }
diff --git a/core/java/com/android/internal/jank/EventLogTags.logtags b/core/java/com/android/internal/jank/EventLogTags.logtags
new file mode 100644
index 0000000..6139bce
--- /dev/null
+++ b/core/java/com/android/internal/jank/EventLogTags.logtags
@@ -0,0 +1,10 @@
+# See system/core/logcat/event.logtags for a description of the format of this file.
+
+option java_package com.android.internal.jank;
+
+# Marks a request to start tracing a CUJ. Doesn't mean the request was executed.
+37001 jank_cuj_events_begin_request (CUJ Type|1|5)
+# Marks a request to end tracing a CUJ. Doesn't mean the request was executed.
+37002 jank_cuj_events_end_request (CUJ Type|1|5)
+# Marks a request to cancel tracing a CUJ. Doesn't mean the request was executed.
+37003 jank_cuj_events_cancel_request (CUJ Type|1|5)
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index 40d192e..76f33a6 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -541,6 +541,7 @@
     public boolean begin(@NonNull Configuration.Builder builder) {
         try {
             final Configuration config = builder.build();
+            EventLogTags.writeJankCujEventsBeginRequest(config.mCujType);
             final TrackerResult result = new TrackerResult();
             final boolean success = config.getHandler().runWithScissors(
                     () -> result.mResult = beginInternal(config), EXECUTOR_TASK_TIMEOUT);
@@ -614,6 +615,7 @@
      * @return boolean true if the tracker is ended successfully, false otherwise.
      */
     public boolean end(@CujType int cujType) {
+        EventLogTags.writeJankCujEventsEndRequest(cujType);
         FrameTracker tracker = getTracker(cujType);
         // Skip this call since we haven't started a trace yet.
         if (tracker == null) return false;
@@ -651,6 +653,7 @@
      * @return boolean true if the tracker is cancelled successfully, false otherwise.
      */
     public boolean cancel(@CujType int cujType) {
+        EventLogTags.writeJankCujEventsCancelRequest(cujType);
         return cancel(cujType, REASON_CANCEL_NORMAL);
     }
 
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index b5a78b0..b60ec9f 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -631,7 +631,7 @@
                                    jshort density, jobject typed_value,
                                    jboolean resolve_references) {
   ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
-  ResourceTimer _tag(ResourceTimer::Counter::GetResourceValue);
+  ResourceTimer _timer(ResourceTimer::Counter::GetResourceValue);
   auto value = assetmanager->GetResource(static_cast<uint32_t>(resid), false /*may_be_bag*/,
                                          static_cast<uint16_t>(density));
   if (!value.has_value()) {
@@ -1234,7 +1234,7 @@
   }
 
   ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
-  ResourceTimer _tag(ResourceTimer::Counter::RetrieveAttributes);
+  ResourceTimer _timer(ResourceTimer::Counter::RetrieveAttributes);
   ResXMLParser* xml_parser = reinterpret_cast<ResXMLParser*>(xml_parser_ptr);
   auto result =
           RetrieveAttributes(assetmanager.get(), xml_parser, reinterpret_cast<uint32_t*>(attrs),
diff --git a/core/jni/android_window_ScreenCapture.cpp b/core/jni/android_window_ScreenCapture.cpp
index 3bada15..c1929c6 100644
--- a/core/jni/android_window_ScreenCapture.cpp
+++ b/core/jni/android_window_ScreenCapture.cpp
@@ -61,9 +61,8 @@
 } gLayerCaptureArgsClassInfo;
 
 static struct {
-    jclass clazz;
-    jmethodID onScreenCaptureComplete;
-} gScreenCaptureListenerClassInfo;
+    jmethodID accept;
+} gConsumerClassInfo;
 
 static struct {
     jclass clazz;
@@ -98,14 +97,14 @@
 public:
     explicit ScreenCaptureListenerWrapper(JNIEnv* env, jobject jobject) {
         env->GetJavaVM(&mVm);
-        mScreenCaptureListenerObject = env->NewGlobalRef(jobject);
-        LOG_ALWAYS_FATAL_IF(!mScreenCaptureListenerObject, "Failed to make global ref");
+        mConsumerObject = env->NewGlobalRef(jobject);
+        LOG_ALWAYS_FATAL_IF(!mConsumerObject, "Failed to make global ref");
     }
 
     ~ScreenCaptureListenerWrapper() {
-        if (mScreenCaptureListenerObject) {
-            getenv()->DeleteGlobalRef(mScreenCaptureListenerObject);
-            mScreenCaptureListenerObject = nullptr;
+        if (mConsumerObject) {
+            getenv()->DeleteGlobalRef(mConsumerObject);
+            mConsumerObject = nullptr;
         }
     }
 
@@ -113,9 +112,8 @@
             const gui::ScreenCaptureResults& captureResults) override {
         JNIEnv* env = getenv();
         if (!captureResults.fenceResult.ok() || captureResults.buffer == nullptr) {
-            env->CallVoidMethod(mScreenCaptureListenerObject,
-                                gScreenCaptureListenerClassInfo.onScreenCaptureComplete, nullptr);
-            checkAndClearException(env, "onScreenCaptureComplete");
+            env->CallVoidMethod(mConsumerObject, gConsumerClassInfo.accept, nullptr);
+            checkAndClearException(env, "accept");
             return binder::Status::ok();
         }
         captureResults.fenceResult.value()->waitForever(LOG_TAG);
@@ -130,17 +128,15 @@
                                             captureResults.capturedSecureLayers,
                                             captureResults.capturedHdrLayers);
         checkAndClearException(env, "builder");
-        env->CallVoidMethod(mScreenCaptureListenerObject,
-                            gScreenCaptureListenerClassInfo.onScreenCaptureComplete,
-                            screenshotHardwareBuffer);
-        checkAndClearException(env, "onScreenCaptureComplete");
+        env->CallVoidMethod(mConsumerObject, gConsumerClassInfo.accept, screenshotHardwareBuffer);
+        checkAndClearException(env, "accept");
         env->DeleteLocalRef(jhardwareBuffer);
         env->DeleteLocalRef(screenshotHardwareBuffer);
         return binder::Status::ok();
     }
 
 private:
-    jobject mScreenCaptureListenerObject;
+    jobject mConsumerObject;
     JavaVM* mVm;
 
     JNIEnv* getenv() {
@@ -194,7 +190,7 @@
 }
 
 static jint nativeCaptureDisplay(JNIEnv* env, jclass clazz, jobject displayCaptureArgsObject,
-                                 jobject screenCaptureListenerObject) {
+                                 jlong screenCaptureListenerObject) {
     const DisplayCaptureArgs captureArgs =
             displayCaptureArgsFromObject(env, displayCaptureArgsObject);
 
@@ -202,13 +198,13 @@
         return BAD_VALUE;
     }
 
-    sp<IScreenCaptureListener> captureListener =
-            sp<ScreenCaptureListenerWrapper>::make(env, screenCaptureListenerObject);
+    sp<gui::IScreenCaptureListener> captureListener =
+            reinterpret_cast<gui::IScreenCaptureListener*>(screenCaptureListenerObject);
     return ScreenshotClient::captureDisplay(captureArgs, captureListener);
 }
 
 static jint nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerCaptureArgsObject,
-                                jobject screenCaptureListenerObject) {
+                                jlong screenCaptureListenerObject) {
     LayerCaptureArgs captureArgs;
     getCaptureArgs(env, layerCaptureArgsObject, captureArgs);
     SurfaceControl* layer = reinterpret_cast<SurfaceControl*>(
@@ -238,21 +234,70 @@
         }
     }
 
-    sp<IScreenCaptureListener> captureListener =
-            sp<ScreenCaptureListenerWrapper>::make(env, screenCaptureListenerObject);
+    sp<gui::IScreenCaptureListener> captureListener =
+            reinterpret_cast<gui::IScreenCaptureListener*>(screenCaptureListenerObject);
     return ScreenshotClient::captureLayers(captureArgs, captureListener);
 }
 
+static jlong nativeCreateScreenCaptureListener(JNIEnv* env, jclass clazz, jobject consumerObj) {
+    sp<gui::IScreenCaptureListener> listener =
+            sp<ScreenCaptureListenerWrapper>::make(env, consumerObj);
+    listener->incStrong((void*)nativeCreateScreenCaptureListener);
+    return reinterpret_cast<jlong>(listener.get());
+}
+
+static void nativeWriteListenerToParcel(JNIEnv* env, jclass clazz, jlong nativeObject,
+                                        jobject parcelObj) {
+    Parcel* parcel = parcelForJavaObject(env, parcelObj);
+    if (parcel == NULL) {
+        jniThrowNullPointerException(env, NULL);
+        return;
+    }
+    ScreenCaptureListenerWrapper* const self =
+            reinterpret_cast<ScreenCaptureListenerWrapper*>(nativeObject);
+    if (self != nullptr) {
+        parcel->writeStrongBinder(IInterface::asBinder(self));
+    }
+}
+
+static jlong nativeReadListenerFromParcel(JNIEnv* env, jclass clazz, jobject parcelObj) {
+    Parcel* parcel = parcelForJavaObject(env, parcelObj);
+    if (parcel == NULL) {
+        jniThrowNullPointerException(env, NULL);
+        return 0;
+    }
+    sp<gui::IScreenCaptureListener> listener =
+            interface_cast<gui::IScreenCaptureListener>(parcel->readStrongBinder());
+    if (listener == nullptr) {
+        return 0;
+    }
+    listener->incStrong((void*)nativeCreateScreenCaptureListener);
+    return reinterpret_cast<jlong>(listener.get());
+}
+
+void destroyNativeListener(void* ptr) {
+    ScreenCaptureListenerWrapper* listener = reinterpret_cast<ScreenCaptureListenerWrapper*>(ptr);
+    listener->decStrong((void*)nativeCreateScreenCaptureListener);
+}
+
+static jlong getNativeListenerFinalizer(JNIEnv* env, jclass clazz) {
+    return static_cast<jlong>(reinterpret_cast<uintptr_t>(&destroyNativeListener));
+}
+
 // ----------------------------------------------------------------------------
 
 static const JNINativeMethod sScreenCaptureMethods[] = {
         // clang-format off
-   {"nativeCaptureDisplay",
-            "(Landroid/window/ScreenCapture$DisplayCaptureArgs;Landroid/window/ScreenCapture$ScreenCaptureListener;)I",
+    {"nativeCaptureDisplay", "(Landroid/window/ScreenCapture$DisplayCaptureArgs;J)I",
             (void*)nativeCaptureDisplay },
-    {"nativeCaptureLayers",
-            "(Landroid/window/ScreenCapture$LayerCaptureArgs;Landroid/window/ScreenCapture$ScreenCaptureListener;)I",
+    {"nativeCaptureLayers",  "(Landroid/window/ScreenCapture$LayerCaptureArgs;J)I",
             (void*)nativeCaptureLayers },
+    {"nativeCreateScreenCaptureListener", "(Ljava/util/function/Consumer;)J",
+            (void*)nativeCreateScreenCaptureListener },
+    {"nativeWriteListenerToParcel", "(JLandroid/os/Parcel;)V", (void*)nativeWriteListenerToParcel },
+    {"nativeReadListenerFromParcel", "(Landroid/os/Parcel;)J",
+            (void*)nativeReadListenerFromParcel },
+    {"getNativeListenerFinalizer", "()J", (void*)getNativeListenerFinalizer },
         // clang-format on
 };
 
@@ -293,12 +338,8 @@
     gLayerCaptureArgsClassInfo.childrenOnly =
             GetFieldIDOrDie(env, layerCaptureArgsClazz, "mChildrenOnly", "Z");
 
-    jclass screenCaptureListenerClazz =
-            FindClassOrDie(env, "android/window/ScreenCapture$ScreenCaptureListener");
-    gScreenCaptureListenerClassInfo.clazz = MakeGlobalRefOrDie(env, screenCaptureListenerClazz);
-    gScreenCaptureListenerClassInfo.onScreenCaptureComplete =
-            GetMethodIDOrDie(env, screenCaptureListenerClazz, "onScreenCaptureComplete",
-                             "(Landroid/window/ScreenCapture$ScreenshotHardwareBuffer;)V");
+    jclass consumer = FindClassOrDie(env, "java/util/function/Consumer");
+    gConsumerClassInfo.accept = GetMethodIDOrDie(env, consumer, "accept", "(Ljava/lang/Object;)V");
 
     jclass screenshotGraphicsBufferClazz =
             FindClassOrDie(env, "android/window/ScreenCapture$ScreenshotHardwareBuffer");
diff --git a/core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java b/core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java
index 3ecc7ff..bf65af3 100644
--- a/core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java
+++ b/core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java
@@ -131,7 +131,7 @@
             new Light(1 /* id */, "Light1", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
                         Light.LIGHT_CAPABILITY_BRIGHTNESS),
             new Light(2 /* id */, "Light2", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
-                        Light.LIGHT_CAPABILITY_RGB),
+                        Light.LIGHT_CAPABILITY_COLOR_RGB),
             new Light(3 /* id */, "Light3", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
                         0 /* capabilities */)
         };
@@ -150,13 +150,13 @@
 
         Light[] mockedLights = {
             new Light(1 /* id */, "Light1", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
-                        Light.LIGHT_CAPABILITY_RGB),
+                        Light.LIGHT_CAPABILITY_COLOR_RGB),
             new Light(2 /* id */, "Light2", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
-                        Light.LIGHT_CAPABILITY_RGB),
+                        Light.LIGHT_CAPABILITY_COLOR_RGB),
             new Light(3 /* id */, "Light3", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
-                        Light.LIGHT_CAPABILITY_RGB),
+                        Light.LIGHT_CAPABILITY_COLOR_RGB),
             new Light(4 /* id */, "Light4", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
-                        Light.LIGHT_CAPABILITY_RGB)
+                        Light.LIGHT_CAPABILITY_COLOR_RGB)
         };
         mockLights(mockedLights);
 
@@ -204,7 +204,7 @@
                 new Light(1 /* id */, "Light1", 0 /* ordinal */, Light.LIGHT_TYPE_PLAYER_ID,
                             0 /* capabilities */),
                 new Light(2 /* id */, "Light2", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
-                            Light.LIGHT_CAPABILITY_RGB | Light.LIGHT_CAPABILITY_BRIGHTNESS),
+                            Light.LIGHT_CAPABILITY_COLOR_RGB | Light.LIGHT_CAPABILITY_BRIGHTNESS),
                 new Light(3 /* id */, "Light3", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
                             Light.LIGHT_CAPABILITY_BRIGHTNESS)
         };
@@ -239,9 +239,9 @@
     @Test
     public void testLightCapabilities() throws Exception {
         Light light = new Light(1 /* id */, "Light1", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
-                Light.LIGHT_CAPABILITY_RGB | Light.LIGHT_CAPABILITY_BRIGHTNESS);
+                Light.LIGHT_CAPABILITY_COLOR_RGB | Light.LIGHT_CAPABILITY_BRIGHTNESS);
         assertThat(light.getType()).isEqualTo(Light.LIGHT_TYPE_INPUT);
-        assertThat(light.getCapabilities()).isEqualTo(Light.LIGHT_CAPABILITY_RGB
+        assertThat(light.getCapabilities()).isEqualTo(Light.LIGHT_CAPABILITY_COLOR_RGB
                 | Light.LIGHT_CAPABILITY_BRIGHTNESS);
         assertTrue(light.hasBrightnessControl());
         assertTrue(light.hasRgbControl());
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index 0e67f1f..c6731d1 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -47,9 +47,7 @@
 import java.io.FileDescriptor;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.Optional;
 import java.util.concurrent.Executor;
-import java.util.stream.Stream;
 
 import sun.misc.Cleaner;
 
@@ -1142,6 +1140,16 @@
     }
 
     /**
+     * Sets whether or not the current process is a system or persistent process. Used to influence
+     * the chosen memory usage policy.
+     *
+     * @hide
+     **/
+    public static void setIsSystemOrPersistent() {
+        nSetIsSystemOrPersistent(true);
+    }
+
+    /**
      * Returns true if HardwareRender will produce output.
      *
      * This value is global to the process and affects all uses of HardwareRenderer,
@@ -1204,30 +1212,6 @@
     private static class ProcessInitializer {
         static ProcessInitializer sInstance = new ProcessInitializer();
 
-        // Magic values from android/data_space.h
-        private static final int INTERNAL_DATASPACE_SRGB = 142671872;
-        private static final int INTERNAL_DATASPACE_DISPLAY_P3 = 143261696;
-        private static final int INTERNAL_DATASPACE_SCRGB = 411107328;
-
-        private enum Dataspace {
-            DISPLAY_P3(ColorSpace.Named.DISPLAY_P3, INTERNAL_DATASPACE_DISPLAY_P3),
-            SCRGB(ColorSpace.Named.EXTENDED_SRGB, INTERNAL_DATASPACE_SCRGB),
-            SRGB(ColorSpace.Named.SRGB, INTERNAL_DATASPACE_SRGB);
-
-            private final ColorSpace.Named mColorSpace;
-            private final int mNativeDataspace;
-            Dataspace(ColorSpace.Named colorSpace, int nativeDataspace) {
-                this.mColorSpace = colorSpace;
-                this.mNativeDataspace = nativeDataspace;
-            }
-
-            static Optional<Dataspace> find(ColorSpace colorSpace) {
-                return Stream.of(Dataspace.values())
-                        .filter(d -> ColorSpace.get(d.mColorSpace).equals(colorSpace))
-                        .findFirst();
-            }
-        }
-
         private boolean mInitialized = false;
         private boolean mDisplayInitialized = false;
 
@@ -1296,6 +1280,7 @@
             initDisplayInfo();
 
             nSetIsHighEndGfx(ActivityManager.isHighEndGfx());
+            nSetIsLowRam(ActivityManager.isLowRamDeviceStatic());
             // Defensively clear out the context in case we were passed a context that can leak
             // if we live longer than it, e.g. an activity context.
             mContext = null;
@@ -1314,26 +1299,55 @@
                 return;
             }
 
-            Display display = dm.getDisplay(Display.DEFAULT_DISPLAY);
-            if (display == null) {
+            final Display defaultDisplay = dm.getDisplay(Display.DEFAULT_DISPLAY);
+            if (defaultDisplay == null) {
                 Log.d(LOG_TAG, "Failed to find default display for display-based configuration");
                 return;
             }
 
-            Dataspace wideColorDataspace =
-                    Optional.ofNullable(display.getPreferredWideGamutColorSpace())
-                            .flatMap(Dataspace::find)
-                            // Default to SRGB if the display doesn't support wide color
-                            .orElse(Dataspace.SRGB);
+            final Display[] allDisplays = dm.getDisplays();
+            if (allDisplays.length == 0) {
+                Log.d(LOG_TAG, "Failed to query displays");
+                return;
+            }
 
-            // Grab the physical screen dimensions from the active display mode
-            // Strictly speaking the screen resolution may not always be constant - it is for
-            // sizing the font cache for the underlying rendering thread. Since it's a
-            // heuristic we don't need to be always 100% correct.
-            Mode activeMode = display.getMode();
-            nInitDisplayInfo(activeMode.getPhysicalWidth(), activeMode.getPhysicalHeight(),
-                    display.getRefreshRate(), wideColorDataspace.mNativeDataspace,
-                    display.getAppVsyncOffsetNanos(), display.getPresentationDeadlineNanos());
+            final Mode activeMode = defaultDisplay.getMode();
+            final ColorSpace defaultWideColorSpace =
+                    defaultDisplay.getPreferredWideGamutColorSpace();
+            int wideColorDataspace = defaultWideColorSpace != null
+                    ? defaultWideColorSpace.getDataSpace() : 0;
+            // largest width & height are used to size the default HWUI cache sizes. So find the
+            // largest display resolution we could encounter & use that as the guidance. The actual
+            // memory policy in play will interpret these values differently.
+            int largestWidth = activeMode.getPhysicalWidth();
+            int largestHeight = activeMode.getPhysicalHeight();
+
+            for (int i = 0; i < allDisplays.length; i++) {
+                final Display display = allDisplays[i];
+                // Take the first wide gamut dataspace as the source of truth
+                // Possibly should do per-HardwareRenderer wide gamut dataspace so we can use the
+                // target display's ideal instead
+                if (wideColorDataspace == 0) {
+                    ColorSpace cs = display.getPreferredWideGamutColorSpace();
+                    if (cs != null) {
+                        wideColorDataspace = cs.getDataSpace();
+                    }
+                }
+                Mode[] modes = display.getSupportedModes();
+                for (int j = 0; j < modes.length; j++) {
+                    Mode mode = modes[j];
+                    int width = mode.getPhysicalWidth();
+                    int height = mode.getPhysicalHeight();
+                    if ((width * height) > (largestWidth * largestHeight)) {
+                        largestWidth = width;
+                        largestHeight = height;
+                    }
+                }
+            }
+
+            nInitDisplayInfo(largestWidth, largestHeight, defaultDisplay.getRefreshRate(),
+                    wideColorDataspace, defaultDisplay.getAppVsyncOffsetNanos(),
+                    defaultDisplay.getPresentationDeadlineNanos());
 
             mDisplayInitialized = true;
         }
@@ -1418,6 +1432,10 @@
 
     private static native void nSetIsHighEndGfx(boolean isHighEndGfx);
 
+    private static native void nSetIsLowRam(boolean isLowRam);
+
+    private static native void nSetIsSystemOrPersistent(boolean isSystemOrPersistent);
+
     private static native int nSyncAndDrawFrame(long nativeProxy, long[] frameInfo, int size);
 
     private static native void nDestroy(long nativeProxy, long rootRenderNode);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index f4dda4c..80cdd1f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -484,12 +484,14 @@
             ShellInit shellInit,
             ShellCommandHandler shellCommandHandler,
             TaskStackListenerImpl taskStackListener,
+            ActivityTaskManager activityTaskManager,
             Optional<DesktopModeTaskRepository> desktopModeTaskRepository,
             @ShellMainThread ShellExecutor mainExecutor
     ) {
         return Optional.ofNullable(
                 RecentTasksController.create(context, shellInit, shellCommandHandler,
-                        taskStackListener, desktopModeTaskRepository, mainExecutor));
+                        taskStackListener, activityTaskManager, desktopModeTaskRepository,
+                        mainExecutor));
     }
 
     //
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index e784261..37a50b6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -188,14 +188,16 @@
             @ShellMainThread Choreographer mainChoreographer,
             ShellTaskOrganizer taskOrganizer,
             DisplayController displayController,
-            SyncTransactionQueue syncQueue) {
+            SyncTransactionQueue syncQueue,
+            @DynamicOverride DesktopModeController desktopModeController) {
         return new CaptionWindowDecorViewModel(
                         context,
                         mainHandler,
                         mainChoreographer,
                         taskOrganizer,
                         displayController,
-                        syncQueue);
+                        syncQueue,
+                        desktopModeController);
     }
 
     //
@@ -318,6 +320,7 @@
             ShellCommandHandler shellCommandHandler,
             ShellController shellController,
             DisplayController displayController,
+            PipAnimationController pipAnimationController,
             PipAppOpsListener pipAppOpsListener,
             PipBoundsAlgorithm pipBoundsAlgorithm,
             PhonePipKeepClearAlgorithm pipKeepClearAlgorithm,
@@ -337,11 +340,12 @@
             @ShellMainThread ShellExecutor mainExecutor) {
         return Optional.ofNullable(PipController.create(
                 context, shellInit, shellCommandHandler, shellController,
-                displayController, pipAppOpsListener, pipBoundsAlgorithm, pipKeepClearAlgorithm,
-                pipBoundsState, pipMotionHelper, pipMediaController, phonePipMenuController,
-                pipTaskOrganizer, pipTransitionState, pipTouchHandler, pipTransitionController,
-                windowManagerShellWrapper, taskStackListener, pipParamsChangedForwarder,
-                displayInsetsController, oneHandedController, mainExecutor));
+                displayController, pipAnimationController, pipAppOpsListener, pipBoundsAlgorithm,
+                pipKeepClearAlgorithm, pipBoundsState, pipMotionHelper, pipMediaController,
+                phonePipMenuController, pipTaskOrganizer, pipTransitionState, pipTouchHandler,
+                pipTransitionController, windowManagerShellWrapper, taskStackListener,
+                pipParamsChangedForwarder, displayInsetsController, oneHandedController,
+                mainExecutor));
     }
 
     @WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java
index 9474cfe..99739c4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java
@@ -177,6 +177,25 @@
     }
 
     /**
+     * Turn desktop mode on or off
+     * @param active the desired state for desktop mode setting
+     */
+    public void setDesktopModeActive(boolean active) {
+        int value = active ? 1 : 0;
+        Settings.System.putInt(mContext.getContentResolver(), Settings.System.DESKTOP_MODE, value);
+    }
+
+    /**
+     * Returns the windowing mode of the display area with the specified displayId.
+     * @param displayId
+     * @return
+     */
+    public int getDisplayAreaWindowingMode(int displayId) {
+        return mRootTaskDisplayAreaOrganizer.getDisplayAreaInfo(displayId)
+                .configuration.windowConfiguration.getWindowingMode();
+    }
+
+    /**
      * A {@link ContentObserver} for listening to changes to {@link Settings.System#DESKTOP_MODE}
      */
     private final class SettingsObserver extends ContentObserver {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index b32c3ee..6728c00 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -195,6 +195,17 @@
     }
 
     /**
+     * Returns true if the PiP window is currently being animated.
+     */
+    public boolean isAnimating() {
+        PipAnimationController.PipTransitionAnimator animator = getCurrentAnimator();
+        if (animator != null && animator.isRunning()) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
      * Quietly cancel the animator by removing the listeners first.
      */
     static void quietCancel(@NonNull ValueAnimator animator) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 1a52d8c..f170e77 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -324,19 +324,6 @@
         return mPipTransitionController;
     }
 
-    /**
-     * Returns true if the PiP window is currently being animated.
-     */
-    public boolean isAnimating() {
-        // TODO(b/183746978) move this to PipAnimationController, and inject that in PipController
-        PipAnimationController.PipTransitionAnimator animator =
-                mPipAnimationController.getCurrentAnimator();
-        if (animator != null && animator.isRunning()) {
-            return true;
-        }
-        return false;
-    }
-
     public Rect getCurrentOrAnimatingBounds() {
         PipAnimationController.PipTransitionAnimator animator =
                 mPipAnimationController.getCurrentAnimator();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index bc8191d..af47666 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -130,6 +130,7 @@
     private DisplayController mDisplayController;
     private PipInputConsumer mPipInputConsumer;
     private WindowManagerShellWrapper mWindowManagerShellWrapper;
+    private PipAnimationController mPipAnimationController;
     private PipAppOpsListener mAppOpsListener;
     private PipMediaController mMediaController;
     private PipBoundsAlgorithm mPipBoundsAlgorithm;
@@ -158,7 +159,7 @@
             return;
         }
         // if there is another animation ongoing, wait for it to finish and try again
-        if (mPipTaskOrganizer.isAnimating()) {
+        if (mPipAnimationController.isAnimating()) {
             mMainExecutor.removeCallbacks(
                     mMovePipInResponseToKeepClearAreasChangeCallback);
             mMainExecutor.executeDelayed(
@@ -368,6 +369,7 @@
             ShellCommandHandler shellCommandHandler,
             ShellController shellController,
             DisplayController displayController,
+            PipAnimationController pipAnimationController,
             PipAppOpsListener pipAppOpsListener,
             PipBoundsAlgorithm pipBoundsAlgorithm,
             PipKeepClearAlgorithm pipKeepClearAlgorithm,
@@ -392,11 +394,12 @@
         }
 
         return new PipController(context, shellInit, shellCommandHandler, shellController,
-                displayController, pipAppOpsListener, pipBoundsAlgorithm, pipKeepClearAlgorithm,
-                pipBoundsState, pipMotionHelper, pipMediaController, phonePipMenuController,
-                pipTaskOrganizer, pipTransitionState, pipTouchHandler, pipTransitionController,
-                windowManagerShellWrapper, taskStackListener, pipParamsChangedForwarder,
-                displayInsetsController, oneHandedController, mainExecutor)
+                displayController, pipAnimationController, pipAppOpsListener,
+                pipBoundsAlgorithm, pipKeepClearAlgorithm, pipBoundsState, pipMotionHelper,
+                pipMediaController, phonePipMenuController, pipTaskOrganizer, pipTransitionState,
+                pipTouchHandler, pipTransitionController, windowManagerShellWrapper,
+                taskStackListener, pipParamsChangedForwarder, displayInsetsController,
+                oneHandedController, mainExecutor)
                 .mImpl;
     }
 
@@ -405,6 +408,7 @@
             ShellCommandHandler shellCommandHandler,
             ShellController shellController,
             DisplayController displayController,
+            PipAnimationController pipAnimationController,
             PipAppOpsListener pipAppOpsListener,
             PipBoundsAlgorithm pipBoundsAlgorithm,
             PipKeepClearAlgorithm pipKeepClearAlgorithm,
@@ -445,6 +449,7 @@
         mMediaController = pipMediaController;
         mMenuController = phonePipMenuController;
         mTouchHandler = pipTouchHandler;
+        mPipAnimationController = pipAnimationController;
         mAppOpsListener = pipAppOpsListener;
         mOneHandedController = oneHandedController;
         mPipTransitionController = pipTransitionController;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java
index a5748f6..2a62552 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java
@@ -17,6 +17,11 @@
 package com.android.wm.shell.recents;
 
 import com.android.wm.shell.common.annotations.ExternalThread;
+import com.android.wm.shell.util.GroupedRecentTaskInfo;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
 
 /**
  * Interface for interacting with the recent tasks.
@@ -29,4 +34,11 @@
     default IRecentTasks createExternalInterface() {
         return null;
     }
+
+    /**
+     * Gets the set of recent tasks.
+     */
+    default void getRecentTasks(int maxNum, int flags, int userId, Executor callbackExecutor,
+            Consumer<List<GroupedRecentTaskInfo>> callback) {
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index 6409e70..02b5a35 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -58,6 +58,8 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
 
 /**
  * Manages the recent task list from the system, caching it as necessary.
@@ -72,6 +74,7 @@
     private final ShellExecutor mMainExecutor;
     private final TaskStackListenerImpl mTaskStackListener;
     private final RecentTasks mImpl = new RecentTasksImpl();
+    private final ActivityTaskManager mActivityTaskManager;
     private IRecentTasksListener mListener;
     private final boolean mIsDesktopMode;
 
@@ -96,6 +99,7 @@
             ShellInit shellInit,
             ShellCommandHandler shellCommandHandler,
             TaskStackListenerImpl taskStackListener,
+            ActivityTaskManager activityTaskManager,
             Optional<DesktopModeTaskRepository> desktopModeTaskRepository,
             @ShellMainThread ShellExecutor mainExecutor
     ) {
@@ -103,17 +107,19 @@
             return null;
         }
         return new RecentTasksController(context, shellInit, shellCommandHandler, taskStackListener,
-                desktopModeTaskRepository, mainExecutor);
+                activityTaskManager, desktopModeTaskRepository, mainExecutor);
     }
 
     RecentTasksController(Context context,
             ShellInit shellInit,
             ShellCommandHandler shellCommandHandler,
             TaskStackListenerImpl taskStackListener,
+            ActivityTaskManager activityTaskManager,
             Optional<DesktopModeTaskRepository> desktopModeTaskRepository,
             ShellExecutor mainExecutor) {
         mContext = context;
         mShellCommandHandler = shellCommandHandler;
+        mActivityTaskManager = activityTaskManager;
         mIsDesktopMode = mContext.getPackageManager().hasSystemFeature(FEATURE_PC);
         mTaskStackListener = taskStackListener;
         mDesktopModeTaskRepository = desktopModeTaskRepository;
@@ -270,15 +276,10 @@
     }
 
     @VisibleForTesting
-    List<ActivityManager.RecentTaskInfo> getRawRecentTasks(int maxNum, int flags, int userId) {
-        return ActivityTaskManager.getInstance().getRecentTasks(maxNum, flags, userId);
-    }
-
-    @VisibleForTesting
     ArrayList<GroupedRecentTaskInfo> getRecentTasks(int maxNum, int flags, int userId) {
         // Note: the returned task list is from the most-recent to least-recent order
-        final List<ActivityManager.RecentTaskInfo> rawList = getRawRecentTasks(maxNum, flags,
-                userId);
+        final List<ActivityManager.RecentTaskInfo> rawList = mActivityTaskManager.getRecentTasks(
+                maxNum, flags, userId);
 
         // Make a mapping of task id -> task info
         final SparseArray<ActivityManager.RecentTaskInfo> rawMapping = new SparseArray<>();
@@ -335,8 +336,9 @@
         if (componentName == null) {
             return null;
         }
-        List<ActivityManager.RecentTaskInfo> tasks = getRawRecentTasks(Integer.MAX_VALUE,
-                ActivityManager.RECENT_IGNORE_UNAVAILABLE, ActivityManager.getCurrentUser());
+        List<ActivityManager.RecentTaskInfo> tasks = mActivityTaskManager.getRecentTasks(
+                Integer.MAX_VALUE, ActivityManager.RECENT_IGNORE_UNAVAILABLE,
+                ActivityManager.getCurrentUser());
         for (int i = 0; i < tasks.size(); i++) {
             final ActivityManager.RecentTaskInfo task = tasks.get(i);
             if (task.isVisible) {
@@ -374,6 +376,16 @@
             mIRecentTasks = new IRecentTasksImpl(RecentTasksController.this);
             return mIRecentTasks;
         }
+
+        @Override
+        public void getRecentTasks(int maxNum, int flags, int userId, Executor executor,
+                Consumer<List<GroupedRecentTaskInfo>> callback) {
+            mMainExecutor.execute(() -> {
+                List<GroupedRecentTaskInfo> tasks =
+                        RecentTasksController.this.getRecentTasks(maxNum, flags, userId);
+                executor.execute(() -> callback.accept(tasks));
+            });
+        }
     }
 
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index 9c7131a..9e49b51 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.windowdecor;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -36,6 +37,7 @@
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.desktopmode.DesktopModeController;
 import com.android.wm.shell.desktopmode.DesktopModeStatus;
 import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
 import com.android.wm.shell.transition.Transitions;
@@ -53,6 +55,7 @@
     private final DisplayController mDisplayController;
     private final SyncTransactionQueue mSyncQueue;
     private FreeformTaskTransitionStarter mTransitionStarter;
+    private DesktopModeController mDesktopModeController;
 
     public CaptionWindowDecorViewModel(
             Context context,
@@ -60,7 +63,8 @@
             Choreographer mainChoreographer,
             ShellTaskOrganizer taskOrganizer,
             DisplayController displayController,
-            SyncTransactionQueue syncQueue) {
+            SyncTransactionQueue syncQueue,
+            DesktopModeController desktopModeController) {
         mContext = context;
         mMainHandler = mainHandler;
         mMainChoreographer = mainChoreographer;
@@ -68,6 +72,7 @@
         mTaskOrganizer = taskOrganizer;
         mDisplayController = displayController;
         mSyncQueue = syncQueue;
+        mDesktopModeController = desktopModeController;
     }
 
     @Override
@@ -210,8 +215,10 @@
         }
 
         private void handleEventForMove(MotionEvent e) {
-            if (mTaskOrganizer.getRunningTaskInfo(mTaskId).getWindowingMode()
-                    == WINDOWING_MODE_FULLSCREEN) {
+            RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
+            int windowingMode =  mDesktopModeController
+                    .getDisplayAreaWindowingMode(taskInfo.displayId);
+            if (windowingMode == WINDOWING_MODE_FULLSCREEN) {
                 return;
             }
             switch (e.getActionMasked()) {
@@ -229,8 +236,14 @@
                 case MotionEvent.ACTION_UP:
                 case MotionEvent.ACTION_CANCEL: {
                     int dragPointerIdx = e.findPointerIndex(mDragPointerId);
+                    int statusBarHeight = mDisplayController.getDisplayLayout(taskInfo.displayId)
+                            .stableInsets().top;
                     mDragResizeCallback.onDragResizeEnd(
                             e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
+                    if (e.getRawY(dragPointerIdx) <= statusBarHeight
+                            && windowingMode == WINDOWING_MODE_FREEFORM) {
+                        mDesktopModeController.setDesktopModeActive(false);
+                    }
                     break;
                 }
             }
@@ -240,6 +253,7 @@
     private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) {
         if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) return true;
         return DesktopModeStatus.IS_SUPPORTED
+                && taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD
                 && mDisplayController.getDisplayContext(taskInfo.displayId)
                 .getResources().getConfiguration().smallestScreenWidthDp >= 600;
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index a8d3bdc..1e08f1e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -48,6 +48,7 @@
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.TaskStackListenerImpl;
 import com.android.wm.shell.onehanded.OneHandedController;
+import com.android.wm.shell.pip.PipAnimationController;
 import com.android.wm.shell.pip.PipAppOpsListener;
 import com.android.wm.shell.pip.PipBoundsAlgorithm;
 import com.android.wm.shell.pip.PipBoundsState;
@@ -85,6 +86,7 @@
     @Mock private ShellCommandHandler mMockShellCommandHandler;
     @Mock private DisplayController mMockDisplayController;
     @Mock private PhonePipMenuController mMockPhonePipMenuController;
+    @Mock private PipAnimationController mMockPipAnimationController;
     @Mock private PipAppOpsListener mMockPipAppOpsListener;
     @Mock private PipBoundsAlgorithm mMockPipBoundsAlgorithm;
     @Mock private PhonePipKeepClearAlgorithm mMockPipKeepClearAlgorithm;
@@ -117,8 +119,8 @@
         mShellController = spy(new ShellController(mShellInit, mMockShellCommandHandler,
                 mMockExecutor));
         mPipController = new PipController(mContext, mShellInit, mMockShellCommandHandler,
-                mShellController, mMockDisplayController, mMockPipAppOpsListener,
-                mMockPipBoundsAlgorithm, mMockPipKeepClearAlgorithm,
+                mShellController, mMockDisplayController, mMockPipAnimationController,
+                mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipKeepClearAlgorithm,
                 mMockPipBoundsState, mMockPipMotionHelper, mMockPipMediaController,
                 mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTransitionState,
                 mMockPipTouchHandler, mMockPipTransitionController, mMockWindowManagerShellWrapper,
@@ -183,8 +185,8 @@
 
         ShellInit shellInit = new ShellInit(mMockExecutor);
         assertNull(PipController.create(spyContext, shellInit, mMockShellCommandHandler,
-                mShellController, mMockDisplayController, mMockPipAppOpsListener,
-                mMockPipBoundsAlgorithm, mMockPipKeepClearAlgorithm,
+                mShellController, mMockDisplayController, mMockPipAnimationController,
+                mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipKeepClearAlgorithm,
                 mMockPipBoundsState, mMockPipMotionHelper, mMockPipMediaController,
                 mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTransitionState,
                 mMockPipTouchHandler, mMockPipTransitionController, mMockWindowManagerShellWrapper,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index 70fee2b..b8aaaa7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -40,6 +40,7 @@
 import static java.lang.Integer.MAX_VALUE;
 
 import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.graphics.Rect;
@@ -52,7 +53,6 @@
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.TestShellExecutor;
-import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.TaskStackListenerImpl;
 import com.android.wm.shell.desktopmode.DesktopModeStatus;
 import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
@@ -68,7 +68,9 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 import java.util.Optional;
+import java.util.function.Consumer;
 
 /**
  * Tests for {@link RecentTasksController}.
@@ -85,11 +87,13 @@
     private ShellCommandHandler mShellCommandHandler;
     @Mock
     private DesktopModeTaskRepository mDesktopModeTaskRepository;
+    @Mock
+    private ActivityTaskManager mActivityTaskManager;
 
     private ShellTaskOrganizer mShellTaskOrganizer;
     private RecentTasksController mRecentTasksController;
     private ShellInit mShellInit;
-    private ShellExecutor mMainExecutor;
+    private TestShellExecutor mMainExecutor;
 
     @Before
     public void setUp() {
@@ -97,8 +101,8 @@
         when(mContext.getPackageManager()).thenReturn(mock(PackageManager.class));
         mShellInit = spy(new ShellInit(mMainExecutor));
         mRecentTasksController = spy(new RecentTasksController(mContext, mShellInit,
-                mShellCommandHandler, mTaskStackListener, Optional.of(mDesktopModeTaskRepository),
-                mMainExecutor));
+                mShellCommandHandler, mTaskStackListener, mActivityTaskManager,
+                Optional.of(mDesktopModeTaskRepository), mMainExecutor));
         mShellTaskOrganizer = new ShellTaskOrganizer(mShellInit, mShellCommandHandler,
                 null /* sizeCompatUI */, Optional.empty(), Optional.of(mRecentTasksController),
                 mMainExecutor);
@@ -188,6 +192,37 @@
     }
 
     @Test
+    public void testGetRecentTasks_ReturnsRecentTasksAsynchronously() {
+        @SuppressWarnings("unchecked")
+        final List<GroupedRecentTaskInfo>[] recentTasks = new List[1];
+        Consumer<List<GroupedRecentTaskInfo>> consumer = argument -> recentTasks[0] = argument;
+        ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
+        ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
+        ActivityManager.RecentTaskInfo t3 = makeTaskInfo(3);
+        ActivityManager.RecentTaskInfo t4 = makeTaskInfo(4);
+        ActivityManager.RecentTaskInfo t5 = makeTaskInfo(5);
+        ActivityManager.RecentTaskInfo t6 = makeTaskInfo(6);
+        setRawList(t1, t2, t3, t4, t5, t6);
+
+        // Mark a couple pairs [t2, t4], [t3, t5]
+        SplitBounds pair1Bounds = new SplitBounds(new Rect(), new Rect(), 2, 4);
+        SplitBounds pair2Bounds = new SplitBounds(new Rect(), new Rect(), 3, 5);
+
+        mRecentTasksController.addSplitPair(t2.taskId, t4.taskId, pair1Bounds);
+        mRecentTasksController.addSplitPair(t3.taskId, t5.taskId, pair2Bounds);
+
+        mRecentTasksController.asRecentTasks()
+                .getRecentTasks(MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0, Runnable::run, consumer);
+        mMainExecutor.flushAll();
+
+        assertGroupedTasksListEquals(recentTasks[0],
+                t1.taskId, -1,
+                t2.taskId, t4.taskId,
+                t3.taskId, t5.taskId,
+                t6.taskId, -1);
+    }
+
+    @Test
     public void testGetRecentTasks_groupActiveFreeformTasks() {
         StaticMockitoSession mockitoSession = mockitoSession().mockStatic(
                 DesktopModeStatus.class).startMocking();
@@ -296,7 +331,7 @@
         for (ActivityManager.RecentTaskInfo task : tasks) {
             rawList.add(task);
         }
-        doReturn(rawList).when(mRecentTasksController).getRawRecentTasks(anyInt(), anyInt(),
+        doReturn(rawList).when(mActivityTaskManager).getRecentTasks(anyInt(), anyInt(),
                 anyInt());
         return rawList;
     }
@@ -307,7 +342,7 @@
      * @param expectedTaskIds list of task ids that map to the flattened task ids of the tasks in
      *                        the grouped task list
      */
-    private void assertGroupedTasksListEquals(ArrayList<GroupedRecentTaskInfo> recentTasks,
+    private void assertGroupedTasksListEquals(List<GroupedRecentTaskInfo> recentTasks,
             int... expectedTaskIds) {
         int[] flattenedTaskIds = new int[recentTasks.size() * 2];
         for (int i = 0; i < recentTasks.size(); i++) {
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index b11e542..29f3773 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -521,6 +521,7 @@
         "Interpolator.cpp",
         "LightingInfo.cpp",
         "Matrix.cpp",
+        "MemoryPolicy.cpp",
         "PathParser.cpp",
         "Properties.cpp",
         "PropertyValuesAnimatorSet.cpp",
diff --git a/libs/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp
index 0759471..f06fa24 100644
--- a/libs/hwui/DeviceInfo.cpp
+++ b/libs/hwui/DeviceInfo.cpp
@@ -93,12 +93,14 @@
         case ADATASPACE_SCRGB:
             get()->mWideColorSpace = SkColorSpace::MakeSRGB();
             break;
+        default:
+            ALOGW("Unknown dataspace %d", dataspace);
+            // Treat unknown dataspaces as sRGB, so fall through
+            [[fallthrough]];
         case ADATASPACE_SRGB:
             // when sRGB is returned, it means wide color gamut is not supported.
             get()->mWideColorSpace = SkColorSpace::MakeSRGB();
             break;
-        default:
-            LOG_ALWAYS_FATAL("Unreachable: unsupported wide color space.");
     }
 }
 
diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp
index 7291cab..b7e9999 100644
--- a/libs/hwui/HardwareBitmapUploader.cpp
+++ b/libs/hwui/HardwareBitmapUploader.cpp
@@ -42,6 +42,8 @@
 
 namespace android::uirenderer {
 
+static constexpr auto kThreadTimeout = 60000_ms;
+
 class AHBUploader;
 // This helper uploader classes allows us to upload using either EGL or Vulkan using the same
 // interface.
@@ -80,7 +82,7 @@
     }
 
     void postIdleTimeoutCheck() {
-        mUploadThread->queue().postDelayed(5000_ms, [this](){ this->idleTimeoutCheck(); });
+        mUploadThread->queue().postDelayed(kThreadTimeout, [this]() { this->idleTimeoutCheck(); });
     }
 
 protected:
@@ -97,7 +99,7 @@
 
     bool shouldTimeOutLocked() {
         nsecs_t durationSince = systemTime() - mLastUpload;
-        return durationSince > 2000_ms;
+        return durationSince > kThreadTimeout;
     }
 
     void idleTimeoutCheck() {
diff --git a/libs/hwui/MemoryPolicy.cpp b/libs/hwui/MemoryPolicy.cpp
new file mode 100644
index 0000000..ca1312e7
--- /dev/null
+++ b/libs/hwui/MemoryPolicy.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include "MemoryPolicy.h"
+
+#include <android-base/properties.h>
+
+#include <optional>
+#include <string_view>
+
+#include "Properties.h"
+
+namespace android::uirenderer {
+
+constexpr static MemoryPolicy sDefaultMemoryPolicy;
+constexpr static MemoryPolicy sPersistentOrSystemPolicy{
+        .contextTimeout = 10_s,
+        .useAlternativeUiHidden = true,
+};
+constexpr static MemoryPolicy sLowRamPolicy{
+        .useAlternativeUiHidden = true,
+        .purgeScratchOnly = false,
+};
+constexpr static MemoryPolicy sExtremeLowRam{
+        .initialMaxSurfaceAreaScale = 0.2f,
+        .surfaceSizeMultiplier = 5 * 4.0f,
+        .backgroundRetentionPercent = 0.2f,
+        .contextTimeout = 5_s,
+        .minimumResourceRetention = 1_s,
+        .useAlternativeUiHidden = true,
+        .purgeScratchOnly = false,
+        .releaseContextOnStoppedOnly = true,
+};
+
+const MemoryPolicy& loadMemoryPolicy() {
+    if (Properties::isSystemOrPersistent) {
+        return sPersistentOrSystemPolicy;
+    }
+    std::string memoryPolicy = base::GetProperty(PROPERTY_MEMORY_POLICY, "");
+    if (memoryPolicy == "default") {
+        return sDefaultMemoryPolicy;
+    }
+    if (memoryPolicy == "lowram") {
+        return sLowRamPolicy;
+    }
+    if (memoryPolicy == "extremelowram") {
+        return sExtremeLowRam;
+    }
+
+    if (Properties::isLowRam) {
+        return sLowRamPolicy;
+    }
+    return sDefaultMemoryPolicy;
+}
+
+}  // namespace android::uirenderer
\ No newline at end of file
diff --git a/libs/hwui/MemoryPolicy.h b/libs/hwui/MemoryPolicy.h
new file mode 100644
index 0000000..e86b338
--- /dev/null
+++ b/libs/hwui/MemoryPolicy.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#pragma once
+
+#include "utils/TimeUtils.h"
+
+namespace android::uirenderer {
+
+// Values mirror those from ComponentCallbacks2.java
+enum class TrimLevel {
+    COMPLETE = 80,
+    MODERATE = 60,
+    BACKGROUND = 40,
+    UI_HIDDEN = 20,
+    RUNNING_CRITICAL = 15,
+    RUNNING_LOW = 10,
+    RUNNING_MODERATE = 5,
+};
+
+struct MemoryPolicy {
+    // The initial scale factor applied to the display resolution. The default is 1, but
+    // lower values may be used to start with a smaller initial cache size. The cache will
+    // be adjusted if larger frames are actually rendered
+    float initialMaxSurfaceAreaScale = 1.0f;
+    // The foreground cache size multiplier. The surface area of the screen will be multiplied
+    // by this
+    float surfaceSizeMultiplier = 12.0f * 4.0f;
+    // How much of the foreground cache size should be preserved when going into the background
+    float backgroundRetentionPercent = 0.5f;
+    // How long after the last renderer goes away before the GPU context is released. A value
+    // of 0 means only drop the context on background TRIM signals
+    nsecs_t contextTimeout = 0_ms;
+    // The minimum amount of time to hold onto items in the resource cache
+    // The actual time used will be the max of this & when frames were actually rendered
+    nsecs_t minimumResourceRetention = 10_s;
+    // If false, use only TRIM_UI_HIDDEN to drive background cache limits;
+    // If true, use all signals (such as all contexts are stopped) to drive the limits
+    bool useAlternativeUiHidden = false;
+    // Whether or not to only purge scratch resources when triggering UI Hidden or background
+    // collection
+    bool purgeScratchOnly = true;
+    // EXPERIMENTAL: Whether or not to trigger releasing GPU context when all contexts are stopped
+    bool releaseContextOnStoppedOnly = false;
+};
+
+const MemoryPolicy& loadMemoryPolicy();
+
+}  // namespace android::uirenderer
\ No newline at end of file
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 5a67eb9..277955e 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -87,6 +87,10 @@
 
 bool Properties::enableWebViewOverlays = true;
 
+bool Properties::isHighEndGfx = true;
+bool Properties::isLowRam = false;
+bool Properties::isSystemOrPersistent = false;
+
 StretchEffectBehavior Properties::stretchEffectBehavior = StretchEffectBehavior::ShaderHWUI;
 
 DrawingEnabled Properties::drawingEnabled = DrawingEnabled::NotInitialized;
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 2f8c679..96a5176 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -193,6 +193,8 @@
  */
 #define PROPERTY_DRAWING_ENABLED "debug.hwui.drawing_enabled"
 
+#define PROPERTY_MEMORY_POLICY "debug.hwui.app_memory_policy"
+
 ///////////////////////////////////////////////////////////////////////////////
 // Misc
 ///////////////////////////////////////////////////////////////////////////////
@@ -292,16 +294,27 @@
 
     static bool enableWebViewOverlays;
 
+    static bool isHighEndGfx;
+    static bool isLowRam;
+    static bool isSystemOrPersistent;
+
     static StretchEffectBehavior getStretchEffectBehavior() {
         return stretchEffectBehavior;
     }
 
     static void setIsHighEndGfx(bool isHighEndGfx) {
+        Properties::isHighEndGfx = isHighEndGfx;
         stretchEffectBehavior = isHighEndGfx ?
             StretchEffectBehavior::ShaderHWUI :
             StretchEffectBehavior::UniformScale;
     }
 
+    static void setIsLowRam(bool isLowRam) { Properties::isLowRam = isLowRam; }
+
+    static void setIsSystemOrPersistent(bool isSystemOrPersistent) {
+        Properties::isSystemOrPersistent = isSystemOrPersistent;
+    }
+
     /**
      * Used for testing. Typical configuration of stretch behavior is done
      * through setIsHighEndGfx
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index 4f281fc..704fba9 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -271,6 +271,16 @@
     Properties::setIsHighEndGfx(jIsHighEndGfx);
 }
 
+static void android_view_ThreadedRenderer_setIsLowRam(JNIEnv* env, jobject clazz,
+                                                      jboolean isLowRam) {
+    Properties::setIsLowRam(isLowRam);
+}
+
+static void android_view_ThreadedRenderer_setIsSystemOrPersistent(JNIEnv* env, jobject clazz,
+                                                                  jboolean isSystemOrPersistent) {
+    Properties::setIsSystemOrPersistent(isSystemOrPersistent);
+}
+
 static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz,
                                                           jlong proxyPtr, jlongArray frameInfo,
                                                           jint frameInfoSize) {
@@ -949,6 +959,9 @@
         {"nSetColorMode", "(JI)V", (void*)android_view_ThreadedRenderer_setColorMode},
         {"nSetSdrWhitePoint", "(JF)V", (void*)android_view_ThreadedRenderer_setSdrWhitePoint},
         {"nSetIsHighEndGfx", "(Z)V", (void*)android_view_ThreadedRenderer_setIsHighEndGfx},
+        {"nSetIsLowRam", "(Z)V", (void*)android_view_ThreadedRenderer_setIsLowRam},
+        {"nSetIsSystemOrPersistent", "(Z)V",
+         (void*)android_view_ThreadedRenderer_setIsSystemOrPersistent},
         {"nSyncAndDrawFrame", "(J[JI)I", (void*)android_view_ThreadedRenderer_syncAndDrawFrame},
         {"nDestroy", "(JJ)V", (void*)android_view_ThreadedRenderer_destroy},
         {"nRegisterAnimatingRenderNode", "(JJ)V",
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index ded2b06..1d24e71 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -16,6 +16,16 @@
 
 #include "CacheManager.h"
 
+#include <GrContextOptions.h>
+#include <SkExecutor.h>
+#include <SkGraphics.h>
+#include <SkMathPriv.h>
+#include <math.h>
+#include <utils/Trace.h>
+
+#include <set>
+
+#include "CanvasContext.h"
 #include "DeviceInfo.h"
 #include "Layer.h"
 #include "Properties.h"
@@ -25,40 +35,34 @@
 #include "pipeline/skia/SkiaMemoryTracer.h"
 #include "renderstate/RenderState.h"
 #include "thread/CommonPool.h"
-#include <utils/Trace.h>
-
-#include <GrContextOptions.h>
-#include <SkExecutor.h>
-#include <SkGraphics.h>
-#include <SkMathPriv.h>
-#include <math.h>
-#include <set>
 
 namespace android {
 namespace uirenderer {
 namespace renderthread {
 
-// This multiplier was selected based on historical review of cache sizes relative
-// to the screen resolution. This is meant to be a conservative default based on
-// that analysis. The 4.0f is used because the default pixel format is assumed to
-// be ARGB_8888.
-#define SURFACE_SIZE_MULTIPLIER (12.0f * 4.0f)
-#define BACKGROUND_RETENTION_PERCENTAGE (0.5f)
+CacheManager::CacheManager(RenderThread& thread)
+        : mRenderThread(thread), mMemoryPolicy(loadMemoryPolicy()) {
+    mMaxSurfaceArea = static_cast<size_t>((DeviceInfo::getWidth() * DeviceInfo::getHeight()) *
+                                          mMemoryPolicy.initialMaxSurfaceAreaScale);
+    setupCacheLimits();
+}
 
-CacheManager::CacheManager()
-        : mMaxSurfaceArea(DeviceInfo::getWidth() * DeviceInfo::getHeight())
-        , mMaxResourceBytes(mMaxSurfaceArea * SURFACE_SIZE_MULTIPLIER)
-        , mBackgroundResourceBytes(mMaxResourceBytes * BACKGROUND_RETENTION_PERCENTAGE)
-        // This sets the maximum size for a single texture atlas in the GPU font cache. If
-        // necessary, the cache can allocate additional textures that are counted against the
-        // total cache limits provided to Skia.
-        , mMaxGpuFontAtlasBytes(GrNextSizePow2(mMaxSurfaceArea))
-        // This sets the maximum size of the CPU font cache to be at least the same size as the
-        // total number of GPU font caches (i.e. 4 separate GPU atlases).
-        , mMaxCpuFontCacheBytes(
-                  std::max(mMaxGpuFontAtlasBytes * 4, SkGraphics::GetFontCacheLimit()))
-        , mBackgroundCpuFontCacheBytes(mMaxCpuFontCacheBytes * BACKGROUND_RETENTION_PERCENTAGE) {
+void CacheManager::setupCacheLimits() {
+    mMaxResourceBytes = mMaxSurfaceArea * mMemoryPolicy.surfaceSizeMultiplier;
+    mBackgroundResourceBytes = mMaxResourceBytes * mMemoryPolicy.backgroundRetentionPercent;
+    // This sets the maximum size for a single texture atlas in the GPU font cache. If
+    // necessary, the cache can allocate additional textures that are counted against the
+    // total cache limits provided to Skia.
+    mMaxGpuFontAtlasBytes = GrNextSizePow2(mMaxSurfaceArea);
+    // This sets the maximum size of the CPU font cache to be at least the same size as the
+    // total number of GPU font caches (i.e. 4 separate GPU atlases).
+    mMaxCpuFontCacheBytes = std::max(mMaxGpuFontAtlasBytes * 4, SkGraphics::GetFontCacheLimit());
+    mBackgroundCpuFontCacheBytes = mMaxCpuFontCacheBytes * mMemoryPolicy.backgroundRetentionPercent;
+
     SkGraphics::SetFontCacheLimit(mMaxCpuFontCacheBytes);
+    if (mGrContext) {
+        mGrContext->setResourceCacheLimit(mMaxResourceBytes);
+    }
 }
 
 void CacheManager::reset(sk_sp<GrDirectContext> context) {
@@ -69,6 +73,7 @@
     if (context) {
         mGrContext = std::move(context);
         mGrContext->setResourceCacheLimit(mMaxResourceBytes);
+        mLastDeferredCleanup = systemTime(CLOCK_MONOTONIC);
     }
 }
 
@@ -96,7 +101,7 @@
     contextOptions->fGpuPathRenderers &= ~GpuPathRenderers::kCoverageCounting;
 }
 
-void CacheManager::trimMemory(TrimMemoryMode mode) {
+void CacheManager::trimMemory(TrimLevel mode) {
     if (!mGrContext) {
         return;
     }
@@ -104,21 +109,28 @@
     // flush and submit all work to the gpu and wait for it to finish
     mGrContext->flushAndSubmit(/*syncCpu=*/true);
 
+    if (!Properties::isHighEndGfx && mode >= TrimLevel::MODERATE) {
+        mode = TrimLevel::COMPLETE;
+    }
+
     switch (mode) {
-        case TrimMemoryMode::Complete:
+        case TrimLevel::COMPLETE:
             mGrContext->freeGpuResources();
             SkGraphics::PurgeAllCaches();
+            mRenderThread.destroyRenderingContext();
             break;
-        case TrimMemoryMode::UiHidden:
+        case TrimLevel::UI_HIDDEN:
             // Here we purge all the unlocked scratch resources and then toggle the resources cache
             // limits between the background and max amounts. This causes the unlocked resources
             // that have persistent data to be purged in LRU order.
-            mGrContext->purgeUnlockedResources(true);
             mGrContext->setResourceCacheLimit(mBackgroundResourceBytes);
-            mGrContext->setResourceCacheLimit(mMaxResourceBytes);
             SkGraphics::SetFontCacheLimit(mBackgroundCpuFontCacheBytes);
+            mGrContext->purgeUnlockedResources(mMemoryPolicy.purgeScratchOnly);
+            mGrContext->setResourceCacheLimit(mMaxResourceBytes);
             SkGraphics::SetFontCacheLimit(mMaxCpuFontCacheBytes);
             break;
+        default:
+            break;
     }
 }
 
@@ -147,11 +159,29 @@
 }
 
 void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState) {
+    log.appendFormat(R"(Memory policy:
+  Max surface area: %zu
+  Max resource usage: %.2fMB (x%.0f)
+  Background retention: %.0f%% (altUiHidden = %s)
+)",
+                     mMaxSurfaceArea, mMaxResourceBytes / 1000000.f,
+                     mMemoryPolicy.surfaceSizeMultiplier,
+                     mMemoryPolicy.backgroundRetentionPercent * 100.0f,
+                     mMemoryPolicy.useAlternativeUiHidden ? "true" : "false");
+    if (Properties::isSystemOrPersistent) {
+        log.appendFormat("  IsSystemOrPersistent\n");
+    }
+    log.appendFormat("  GPU Context timeout: %" PRIu64 "\n", ns2s(mMemoryPolicy.contextTimeout));
+    size_t stoppedContexts = 0;
+    for (auto context : mCanvasContexts) {
+        if (context->isStopped()) stoppedContexts++;
+    }
+    log.appendFormat("Contexts: %zu (stopped = %zu)\n", mCanvasContexts.size(), stoppedContexts);
+
     if (!mGrContext) {
-        log.appendFormat("No valid cache instance.\n");
+        log.appendFormat("No GPU context.\n");
         return;
     }
-
     std::vector<skiapipeline::ResourcePair> cpuResourceMap = {
             {"skia/sk_resource_cache/bitmap_", "Bitmaps"},
             {"skia/sk_resource_cache/rrect-blur_", "Masks"},
@@ -199,6 +229,8 @@
 }
 
 void CacheManager::onFrameCompleted() {
+    cancelDestroyContext();
+    mFrameCompletions.next() = systemTime(CLOCK_MONOTONIC);
     if (ATRACE_ENABLED()) {
         static skiapipeline::ATraceMemoryDump tracer;
         tracer.startFrame();
@@ -210,11 +242,82 @@
     }
 }
 
-void CacheManager::performDeferredCleanup(nsecs_t cleanupOlderThanMillis) {
-    if (mGrContext) {
-        mGrContext->performDeferredCleanup(
-            std::chrono::milliseconds(cleanupOlderThanMillis),
-            /* scratchResourcesOnly */true);
+void CacheManager::onThreadIdle() {
+    if (!mGrContext || mFrameCompletions.size() == 0) return;
+
+    const nsecs_t now = systemTime(CLOCK_MONOTONIC);
+    // Rate limiting
+    if ((now - mLastDeferredCleanup) < 25_ms) {
+        mLastDeferredCleanup = now;
+        const nsecs_t frameCompleteNanos = mFrameCompletions[0];
+        const nsecs_t frameDiffNanos = now - frameCompleteNanos;
+        const nsecs_t cleanupMillis =
+                ns2ms(std::max(frameDiffNanos, mMemoryPolicy.minimumResourceRetention));
+        mGrContext->performDeferredCleanup(std::chrono::milliseconds(cleanupMillis),
+                                           mMemoryPolicy.purgeScratchOnly);
+    }
+}
+
+void CacheManager::scheduleDestroyContext() {
+    if (mMemoryPolicy.contextTimeout > 0) {
+        mRenderThread.queue().postDelayed(mMemoryPolicy.contextTimeout,
+                                          [this, genId = mGenerationId] {
+                                              if (mGenerationId != genId) return;
+                                              // GenID should have already stopped this, but just in
+                                              // case
+                                              if (!areAllContextsStopped()) return;
+                                              mRenderThread.destroyRenderingContext();
+                                          });
+    }
+}
+
+void CacheManager::cancelDestroyContext() {
+    if (mIsDestructionPending) {
+        mIsDestructionPending = false;
+        mGenerationId++;
+    }
+}
+
+bool CacheManager::areAllContextsStopped() {
+    for (auto context : mCanvasContexts) {
+        if (!context->isStopped()) return false;
+    }
+    return true;
+}
+
+void CacheManager::checkUiHidden() {
+    if (!mGrContext) return;
+
+    if (mMemoryPolicy.useAlternativeUiHidden && areAllContextsStopped()) {
+        trimMemory(TrimLevel::UI_HIDDEN);
+    }
+}
+
+void CacheManager::registerCanvasContext(CanvasContext* context) {
+    mCanvasContexts.push_back(context);
+    cancelDestroyContext();
+}
+
+void CacheManager::unregisterCanvasContext(CanvasContext* context) {
+    std::erase(mCanvasContexts, context);
+    checkUiHidden();
+    if (mCanvasContexts.empty()) {
+        scheduleDestroyContext();
+    }
+}
+
+void CacheManager::onContextStopped(CanvasContext* context) {
+    checkUiHidden();
+    if (mMemoryPolicy.releaseContextOnStoppedOnly && areAllContextsStopped()) {
+        scheduleDestroyContext();
+    }
+}
+
+void CacheManager::notifyNextFrameSize(int width, int height) {
+    int frameArea = width * height;
+    if (frameArea > mMaxSurfaceArea) {
+        mMaxSurfaceArea = frameArea;
+        setupCacheLimits();
     }
 }
 
diff --git a/libs/hwui/renderthread/CacheManager.h b/libs/hwui/renderthread/CacheManager.h
index af82672..d21ac9b 100644
--- a/libs/hwui/renderthread/CacheManager.h
+++ b/libs/hwui/renderthread/CacheManager.h
@@ -22,7 +22,11 @@
 #endif
 #include <SkSurface.h>
 #include <utils/String8.h>
+
 #include <vector>
+
+#include "MemoryPolicy.h"
+#include "utils/RingBuffer.h"
 #include "utils/TimeUtils.h"
 
 namespace android {
@@ -35,17 +39,15 @@
 
 namespace renderthread {
 
-class IRenderPipeline;
 class RenderThread;
+class CanvasContext;
 
 class CacheManager {
 public:
-    enum class TrimMemoryMode { Complete, UiHidden };
-
 #ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
     void configureContext(GrContextOptions* context, const void* identity, ssize_t size);
 #endif
-    void trimMemory(TrimMemoryMode mode);
+    void trimMemory(TrimLevel mode);
     void trimStaleResources();
     void dumpMemoryUsage(String8& log, const RenderState* renderState = nullptr);
     void getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage);
@@ -53,30 +55,50 @@
     size_t getCacheSize() const { return mMaxResourceBytes; }
     size_t getBackgroundCacheSize() const { return mBackgroundResourceBytes; }
     void onFrameCompleted();
+    void notifyNextFrameSize(int width, int height);
 
-    void performDeferredCleanup(nsecs_t cleanupOlderThanMillis);
+    void onThreadIdle();
+
+    void registerCanvasContext(CanvasContext* context);
+    void unregisterCanvasContext(CanvasContext* context);
+    void onContextStopped(CanvasContext* context);
 
 private:
     friend class RenderThread;
 
-    explicit CacheManager();
+    explicit CacheManager(RenderThread& thread);
+    void setupCacheLimits();
+    bool areAllContextsStopped();
+    void checkUiHidden();
+    void scheduleDestroyContext();
+    void cancelDestroyContext();
 
 #ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
     void reset(sk_sp<GrDirectContext> grContext);
 #endif
     void destroy();
 
-    const size_t mMaxSurfaceArea;
+    RenderThread& mRenderThread;
+    const MemoryPolicy& mMemoryPolicy;
 #ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
     sk_sp<GrDirectContext> mGrContext;
 #endif
 
-    const size_t mMaxResourceBytes;
-    const size_t mBackgroundResourceBytes;
+    size_t mMaxSurfaceArea = 0;
 
-    const size_t mMaxGpuFontAtlasBytes;
-    const size_t mMaxCpuFontCacheBytes;
-    const size_t mBackgroundCpuFontCacheBytes;
+    size_t mMaxResourceBytes = 0;
+    size_t mBackgroundResourceBytes = 0;
+
+    size_t mMaxGpuFontAtlasBytes = 0;
+    size_t mMaxCpuFontCacheBytes = 0;
+    size_t mBackgroundCpuFontCacheBytes = 0;
+
+    std::vector<CanvasContext*> mCanvasContexts;
+    RingBuffer<uint64_t, 100> mFrameCompletions;
+
+    nsecs_t mLastDeferredCleanup = 0;
+    bool mIsDestructionPending = false;
+    uint32_t mGenerationId = 0;
 };
 
 } /* namespace renderthread */
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 75d3ff7..6a0c5a8 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -42,9 +42,6 @@
 #include "utils/GLUtils.h"
 #include "utils/TimeUtils.h"
 
-#define TRIM_MEMORY_COMPLETE 80
-#define TRIM_MEMORY_UI_HIDDEN 20
-
 #define LOG_FRAMETIME_MMA 0
 
 #if LOG_FRAMETIME_MMA
@@ -122,6 +119,7 @@
         , mProfiler(mJankTracker.frames(), thread.timeLord().frameIntervalNanos())
         , mContentDrawBounds(0, 0, 0, 0)
         , mRenderPipeline(std::move(renderPipeline)) {
+    mRenderThread.cacheManager().registerCanvasContext(this);
     rootRenderNode->makeRoot();
     mRenderNodes.emplace_back(rootRenderNode);
     mProfiler.setDensity(DeviceInfo::getDensity());
@@ -133,6 +131,7 @@
         node->clearRoot();
     }
     mRenderNodes.clear();
+    mRenderThread.cacheManager().unregisterCanvasContext(this);
 }
 
 void CanvasContext::addRenderNode(RenderNode* node, bool placeFront) {
@@ -154,6 +153,7 @@
     freePrefetchedLayers();
     destroyHardwareResources();
     mAnimationContext->destroy();
+    mRenderThread.cacheManager().onContextStopped(this);
 }
 
 static void setBufferCount(ANativeWindow* window) {
@@ -251,6 +251,7 @@
             mGenerationID++;
             mRenderThread.removeFrameCallback(this);
             mRenderPipeline->onStop();
+            mRenderThread.cacheManager().onContextStopped(this);
         } else if (mIsDirty && hasSurface()) {
             mRenderThread.postFrameCallback(this);
         }
@@ -461,7 +462,6 @@
 }
 
 void CanvasContext::stopDrawing() {
-    cleanupResources();
     mRenderThread.removeFrameCallback(this);
     mAnimationContext->pauseAnimators();
     mGenerationID++;
@@ -648,25 +648,10 @@
         }
     }
 
-    cleanupResources();
     mRenderThread.cacheManager().onFrameCompleted();
     return mCurrentFrameInfo->get(FrameInfoIndex::DequeueBufferDuration);
 }
 
-void CanvasContext::cleanupResources() {
-    auto& tracker = mJankTracker.frames();
-    auto size = tracker.size();
-    auto capacity = tracker.capacity();
-    if (size == capacity) {
-        nsecs_t nowNanos = systemTime(SYSTEM_TIME_MONOTONIC);
-        nsecs_t frameCompleteNanos =
-            tracker[0].get(FrameInfoIndex::FrameCompleted);
-        nsecs_t frameDiffNanos = nowNanos - frameCompleteNanos;
-        nsecs_t cleanupMillis = ns2ms(std::max(frameDiffNanos, 10_s));
-        mRenderThread.cacheManager().performDeferredCleanup(cleanupMillis);
-    }
-}
-
 void CanvasContext::reportMetricsWithPresentTime() {
     {  // acquire lock
         std::scoped_lock lock(mFrameMetricsReporterMutex);
@@ -790,6 +775,7 @@
     SkISize size;
     size.fWidth = ANativeWindow_getWidth(anw);
     size.fHeight = ANativeWindow_getHeight(anw);
+    mRenderThread.cacheManager().notifyNextFrameSize(size.fWidth, size.fHeight);
     return size;
 }
 
@@ -868,18 +854,6 @@
     }
 }
 
-void CanvasContext::trimMemory(RenderThread& thread, int level) {
-    ATRACE_CALL();
-    if (!thread.getGrContext()) return;
-    ATRACE_CALL();
-    if (level >= TRIM_MEMORY_COMPLETE) {
-        thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete);
-        thread.destroyRenderingContext();
-    } else if (level >= TRIM_MEMORY_UI_HIDDEN) {
-        thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::UiHidden);
-    }
-}
-
 DeferredLayerUpdater* CanvasContext::createTextureLayer() {
     return mRenderPipeline->createTextureLayer();
 }
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 951ee21..748ab96 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -127,6 +127,7 @@
     void setSurfaceControl(ASurfaceControl* surfaceControl);
     bool pauseSurface();
     void setStopped(bool stopped);
+    bool isStopped() { return mStopped || !hasSurface(); }
     bool hasSurface() const { return mNativeSurface.get(); }
     void allocateBuffers();
 
@@ -148,7 +149,6 @@
     void markLayerInUse(RenderNode* node);
 
     void destroyHardwareResources();
-    static void trimMemory(RenderThread& thread, int level);
 
     DeferredLayerUpdater* createTextureLayer();
 
@@ -330,8 +330,6 @@
 
     std::function<bool(int64_t, int64_t, int64_t)> mASurfaceTransactionCallback;
     std::function<void()> mPrepareSurfaceControlForWebviewCallback;
-
-    void cleanupResources();
 };
 
 } /* namespace renderthread */
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index 59c914f..03f02de 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -243,7 +243,9 @@
     mContext->unpinImages();
 
     for (size_t i = 0; i < mLayers.size(); i++) {
-        mLayers[i]->apply();
+        if (mLayers[i]) {
+            mLayers[i]->apply();
+        }
     }
     mLayers.clear();
     mContext->setContentDrawBounds(mContentDrawBounds);
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 40a0bac..3324715 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -196,7 +196,8 @@
     // Avoid creating a RenderThread to do a trimMemory.
     if (RenderThread::hasInstance()) {
         RenderThread& thread = RenderThread::getInstance();
-        thread.queue().post([&thread, level]() { CanvasContext::trimMemory(thread, level); });
+        const auto trimLevel = static_cast<TrimLevel>(level);
+        thread.queue().post([&thread, trimLevel]() { thread.trimMemory(trimLevel); });
     }
 }
 
@@ -205,7 +206,7 @@
         RenderThread& thread = RenderThread::getInstance();
         thread.queue().post([&thread]() {
             if (thread.getGrContext()) {
-                thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete);
+                thread.cacheManager().trimMemory(TrimLevel::COMPLETE);
             }
         });
     }
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 3ff4081..7a7f1ab 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -251,7 +251,7 @@
     mEglManager = new EglManager();
     mRenderState = new RenderState(*this);
     mVkManager = VulkanManager::getInstance();
-    mCacheManager = new CacheManager();
+    mCacheManager = new CacheManager(*this);
 }
 
 void RenderThread::setupFrameInterval() {
@@ -453,6 +453,8 @@
             // next vsync (oops), so none of the callbacks are run.
             requestVsync();
         }
+
+        mCacheManager->onThreadIdle();
     }
 
     return false;
@@ -502,6 +504,11 @@
     HardwareBitmapUploader::initialize();
 }
 
+void RenderThread::trimMemory(TrimLevel level) {
+    ATRACE_CALL();
+    cacheManager().trimMemory(level);
+}
+
 } /* namespace renderthread */
 } /* namespace uirenderer */
 } /* namespace android */
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index c1f6790..0a89e5e 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -17,11 +17,11 @@
 #ifndef RENDERTHREAD_H_
 #define RENDERTHREAD_H_
 
-#include <surface_control_private.h>
 #include <GrDirectContext.h>
 #include <SkBitmap.h>
 #include <cutils/compiler.h>
 #include <private/android/choreographer.h>
+#include <surface_control_private.h>
 #include <thread/ThreadBase.h>
 #include <utils/Looper.h>
 #include <utils/Thread.h>
@@ -31,6 +31,7 @@
 #include <set>
 
 #include "CacheManager.h"
+#include "MemoryPolicy.h"
 #include "ProfileDataContainer.h"
 #include "RenderTask.h"
 #include "TimeLord.h"
@@ -172,6 +173,8 @@
         return mASurfaceControlFunctions;
     }
 
+    void trimMemory(TrimLevel level);
+
     /**
      * isCurrent provides a way to query, if the caller is running on
      * the render thread.
diff --git a/libs/hwui/tests/unit/CacheManagerTests.cpp b/libs/hwui/tests/unit/CacheManagerTests.cpp
index edd3e4e..df06ead 100644
--- a/libs/hwui/tests/unit/CacheManagerTests.cpp
+++ b/libs/hwui/tests/unit/CacheManagerTests.cpp
@@ -58,7 +58,7 @@
     ASSERT_TRUE(SkImage_pinAsTexture(image.get(), grContext));
 
     // attempt to trim all memory while we still hold strong refs
-    renderThread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete);
+    renderThread.cacheManager().trimMemory(TrimLevel::COMPLETE);
     ASSERT_TRUE(0 == grContext->getResourceCachePurgeableBytes());
 
     // free the surfaces
@@ -75,11 +75,11 @@
     ASSERT_TRUE(renderThread.cacheManager().getBackgroundCacheSize() < purgeableBytes);
 
     // UI hidden and make sure only some got purged (unique should remain)
-    renderThread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::UiHidden);
+    renderThread.cacheManager().trimMemory(TrimLevel::UI_HIDDEN);
     ASSERT_TRUE(0 < grContext->getResourceCachePurgeableBytes());
     ASSERT_TRUE(renderThread.cacheManager().getBackgroundCacheSize() > getCacheUsage(grContext));
 
     // complete and make sure all get purged
-    renderThread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete);
+    renderThread.cacheManager().trimMemory(TrimLevel::COMPLETE);
     ASSERT_TRUE(0 == grContext->getResourceCachePurgeableBytes());
 }
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index be9862b..49b314d 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -242,13 +242,14 @@
      * To decode such an image, {@link MediaCodec} decoder for
      * {@link #MIMETYPE_VIDEO_HEVC} shall be used. The client needs to form
      * the correct {@link #MediaFormat} based on additional information in
-     * the track format, and send it to {@link MediaCodec#configure}.
+     * the track format (shown in the next paragraph), and send it to
+     * {@link MediaCodec#configure}.
      *
      * The track's MediaFormat will come with {@link #KEY_WIDTH} and
      * {@link #KEY_HEIGHT} keys, which describes the width and height
      * of the image. If the image doesn't contain grid (i.e. none of
      * {@link #KEY_TILE_WIDTH}, {@link #KEY_TILE_HEIGHT},
-     * {@link #KEY_GRID_ROWS}, {@link #KEY_GRID_COLUMNS} are present}), the
+     * {@link #KEY_GRID_ROWS}, {@link #KEY_GRID_COLUMNS} are present), the
      * track will contain a single sample of coded data for the entire image,
      * and the image width and height should be used to set up the decoder.
      *
@@ -266,6 +267,36 @@
     public static final String MIMETYPE_IMAGE_ANDROID_HEIC = "image/vnd.android.heic";
 
     /**
+     * MIME type for AVIF still image data encoded in AV1.
+     *
+     * To decode such an image, {@link MediaCodec} decoder for
+     * {@link #MIMETYPE_VIDEO_AV1} shall be used. The client needs to form
+     * the correct {@link #MediaFormat} based on additional information in
+     * the track format (shown in the next paragraph), and send it to
+     * {@link MediaCodec#configure}.
+     *
+     * The track's MediaFormat will come with {@link #KEY_WIDTH} and
+     * {@link #KEY_HEIGHT} keys, which describes the width and height
+     * of the image. If the image doesn't contain grid (i.e. none of
+     * {@link #KEY_TILE_WIDTH}, {@link #KEY_TILE_HEIGHT},
+     * {@link #KEY_GRID_ROWS}, {@link #KEY_GRID_COLUMNS} are present), the
+     * track will contain a single sample of coded data for the entire image,
+     * and the image width and height should be used to set up the decoder.
+     *
+     * If the image does come with grid, each sample from the track will
+     * contain one tile in the grid, of which the size is described by
+     * {@link #KEY_TILE_WIDTH} and {@link #KEY_TILE_HEIGHT}. This size
+     * (instead of {@link #KEY_WIDTH} and {@link #KEY_HEIGHT}) should be
+     * used to set up the decoder. The track contains {@link #KEY_GRID_ROWS}
+     * by {@link #KEY_GRID_COLUMNS} samples in row-major, top-row first,
+     * left-to-right order. The output image should be reconstructed by
+     * first tiling the decoding results of the tiles in the correct order,
+     * then trimming (before rotation is applied) on the bottom and right
+     * side, if the tiled area is larger than the image width and height.
+     */
+    public static final String MIMETYPE_IMAGE_AVIF = "image/avif";
+
+    /**
      * MIME type for WebVTT subtitle data.
      */
     public static final String MIMETYPE_TEXT_VTT = "text/vtt";
@@ -485,9 +516,11 @@
 
     /**
      * A key describing the width (in pixels) of each tile of the content in a
-     * {@link #MIMETYPE_IMAGE_ANDROID_HEIC} track. The associated value is an integer.
+     * {@link #MIMETYPE_IMAGE_ANDROID_HEIC} / {@link #MIMETYPE_IMAGE_AVIF} track.
+     * The associated value is an integer.
      *
-     * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} on decoding instructions of such tracks.
+     * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} / {@link #MIMETYPE_IMAGE_AVIF} on decoding
+     * instructions of such tracks.
      *
      * @see #KEY_TILE_HEIGHT
      * @see #KEY_GRID_ROWS
@@ -497,9 +530,11 @@
 
     /**
      * A key describing the height (in pixels) of each tile of the content in a
-     * {@link #MIMETYPE_IMAGE_ANDROID_HEIC} track. The associated value is an integer.
+     * {@link #MIMETYPE_IMAGE_ANDROID_HEIC} / {@link #MIMETYPE_IMAGE_AVIF} track.
+     * The associated value is an integer.
      *
-     * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} on decoding instructions of such tracks.
+     * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} / {@link #MIMETYPE_IMAGE_AVIF} on decoding
+     * instructions of such tracks.
      *
      * @see #KEY_TILE_WIDTH
      * @see #KEY_GRID_ROWS
@@ -509,9 +544,11 @@
 
     /**
      * A key describing the number of grid rows in the content in a
-     * {@link #MIMETYPE_IMAGE_ANDROID_HEIC} track. The associated value is an integer.
+     * {@link #MIMETYPE_IMAGE_ANDROID_HEIC} / {@link #MIMETYPE_IMAGE_AVIF} track.
+     * The associated value is an integer.
      *
-     * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} on decoding instructions of such tracks.
+     * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} / {@link #MIMETYPE_IMAGE_AVIF} on decoding
+     * instructions of such tracks.
      *
      * @see #KEY_TILE_WIDTH
      * @see #KEY_TILE_HEIGHT
@@ -521,9 +558,11 @@
 
     /**
      * A key describing the number of grid columns in the content in a
-     * {@link #MIMETYPE_IMAGE_ANDROID_HEIC} track. The associated value is an integer.
+     * {@link #MIMETYPE_IMAGE_ANDROID_HEIC} / {@link #MIMETYPE_IMAGE_AVIF} track.
+     * The associated value is an integer.
      *
-     * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} on decoding instructions of such tracks.
+     * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} / {@link #MIMETYPE_IMAGE_AVIF} on decoding
+     * instructions of such tracks.
      *
      * @see #KEY_TILE_WIDTH
      * @see #KEY_TILE_HEIGHT
@@ -1350,9 +1389,9 @@
      * selected in the absence of a specific user choice.
      * This is currently used in two scenarios:
      * 1) for subtitle tracks, when the user selected 'Default' for the captioning locale.
-     * 2) for a {@link #MIMETYPE_IMAGE_ANDROID_HEIC} track, indicating the image is the
-     * primary item in the file.
-
+     * 2) for a {@link #MIMETYPE_IMAGE_ANDROID_HEIC} / {@link #MIMETYPE_IMAGE_AVIF} track,
+     * indicating the image is the primary item in the file.
+     *
      * The associated value is an integer, where non-0 means TRUE.  This is an optional
      * field; if not specified, DEFAULT is considered to be FALSE.
      */
diff --git a/packages/SettingsLib/Spa/build.gradle b/packages/SettingsLib/Spa/build.gradle
index f8667ed..6384cad 100644
--- a/packages/SettingsLib/Spa/build.gradle
+++ b/packages/SettingsLib/Spa/build.gradle
@@ -22,7 +22,7 @@
     }
 }
 plugins {
-    id 'com.android.application' version '7.3.0-rc01' apply false
-    id 'com.android.library' version '7.3.0-rc01' apply false
+    id 'com.android.application' version '7.3.0' apply false
+    id 'com.android.library' version '7.3.0' apply false
     id 'org.jetbrains.kotlin.android' version '1.6.10' apply false
 }
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryDebugActivity.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryDebugActivity.kt
index 1ebc5da..332d5a8 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryDebugActivity.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryDebugActivity.kt
@@ -18,8 +18,4 @@
 
 import com.android.settingslib.spa.framework.DebugActivity
 
-class GalleryDebugActivity : DebugActivity(
-    SpaEnvironment.EntryRepository,
-    browseActivityClass = MainActivity::class.java,
-    entryProviderAuthorities = "com.android.spa.gallery.provider",
-)
+class GalleryDebugActivity : DebugActivity(GallerySpaEnvironment)
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryEntryProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryEntryProvider.kt
index d3e0096..5e04861 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryEntryProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryEntryProvider.kt
@@ -18,7 +18,4 @@
 
 import com.android.settingslib.spa.framework.EntryProvider
 
-class GalleryEntryProvider : EntryProvider(
-    SpaEnvironment.EntryRepository,
-    browseActivityClass = MainActivity::class.java,
-)
+class GalleryEntryProvider : EntryProvider(GallerySpaEnvironment)
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/SpaEnvironment.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
similarity index 89%
rename from packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/SpaEnvironment.kt
rename to packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
index 6fe88e1..33c4d77 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/SpaEnvironment.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
@@ -18,9 +18,9 @@
 
 import android.os.Bundle
 import androidx.navigation.NamedNavArgument
-import com.android.settingslib.spa.framework.common.SettingsEntryRepository
 import com.android.settingslib.spa.framework.common.SettingsPage
 import com.android.settingslib.spa.framework.common.SettingsPageProviderRepository
+import com.android.settingslib.spa.framework.common.SpaEnvironment
 import com.android.settingslib.spa.gallery.button.ActionButtonPageProvider
 import com.android.settingslib.spa.gallery.home.HomePageProvider
 import com.android.settingslib.spa.gallery.page.ArgumentPageProvider
@@ -62,9 +62,8 @@
     )
 }
 
-object SpaEnvironment {
-    val PageProviderRepository: SettingsPageProviderRepository by
-    lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
+object GallerySpaEnvironment : SpaEnvironment() {
+    override val pageProviderRepository = lazy {
         SettingsPageProviderRepository(
             allPageProviders = listOf(
                 HomePageProvider,
@@ -88,9 +87,7 @@
         )
     }
 
-    val EntryRepository: SettingsEntryRepository by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
-        SettingsEntryRepository(PageProviderRepository)
-    }
+    override val browseActivityClass = MainActivity::class.java
 
-    // TODO: add other environment setup here.
+    override val entryProviderAuthorities = "com.android.spa.gallery.provider"
 }
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/MainActivity.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/MainActivity.kt
index a063847..5e859ce 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/MainActivity.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/MainActivity.kt
@@ -18,4 +18,4 @@
 
 import com.android.settingslib.spa.framework.BrowseActivity
 
-class MainActivity : BrowseActivity(SpaEnvironment.PageProviderRepository)
+class MainActivity : BrowseActivity(GallerySpaEnvironment)
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
index 0bb631a..138ea02 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
@@ -31,7 +31,7 @@
 import androidx.navigation.compose.rememberNavController
 import androidx.navigation.navArgument
 import com.android.settingslib.spa.R
-import com.android.settingslib.spa.framework.common.SettingsPageProviderRepository
+import com.android.settingslib.spa.framework.common.SpaEnvironment
 import com.android.settingslib.spa.framework.compose.localNavController
 import com.android.settingslib.spa.framework.theme.SettingsTheme
 import com.android.settingslib.spa.framework.util.navRoute
@@ -48,9 +48,9 @@
  *   $ adb shell am start -n <BrowseActivityComponent> -e spa:SpaActivity:destination HOME
  *   $ adb shell am start -n <BrowseActivityComponent> -e spa:SpaActivity:destination ARGUMENT/bar/5
  */
-open class BrowseActivity(
-    private val sppRepository: SettingsPageProviderRepository,
-) : ComponentActivity() {
+open class BrowseActivity(spaEnvironment: SpaEnvironment) : ComponentActivity() {
+    private val sppRepository by spaEnvironment.pageProviderRepository
+
     override fun onCreate(savedInstanceState: Bundle?) {
         setTheme(R.style.Theme_SpaLib_DayNight)
         super.onCreate(savedInstanceState)
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/DebugActivity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/DebugActivity.kt
index c698d9c..85fc366 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/DebugActivity.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/DebugActivity.kt
@@ -35,8 +35,8 @@
 import com.android.settingslib.spa.R
 import com.android.settingslib.spa.framework.BrowseActivity.Companion.KEY_DESTINATION
 import com.android.settingslib.spa.framework.common.SettingsEntry
-import com.android.settingslib.spa.framework.common.SettingsEntryRepository
 import com.android.settingslib.spa.framework.common.SettingsPage
+import com.android.settingslib.spa.framework.common.SpaEnvironment
 import com.android.settingslib.spa.framework.compose.localNavController
 import com.android.settingslib.spa.framework.compose.navigator
 import com.android.settingslib.spa.framework.compose.toState
@@ -61,11 +61,9 @@
  * For gallery, Activity = com.android.settingslib.spa.gallery/.GalleryDebugActivity
  * For SettingsGoogle, Activity = com.android.settings/.spa.SpaDebugActivity
  */
-open class DebugActivity(
-    private val entryRepository: SettingsEntryRepository,
-    private val browseActivityClass: Class<*>,
-    private val entryProviderAuthorities: String? = null,
-) : ComponentActivity() {
+open class DebugActivity(private val spaEnvironment: SpaEnvironment) : ComponentActivity() {
+    private val entryRepository by spaEnvironment.entryRepository
+
     override fun onCreate(savedInstanceState: Bundle?) {
         setTheme(R.style.Theme_SpaLib_DayNight)
         super.onCreate(savedInstanceState)
@@ -79,7 +77,7 @@
     }
 
     private fun displayDebugMessage() {
-        if (entryProviderAuthorities == null) return
+        val entryProviderAuthorities = spaEnvironment.entryProviderAuthorities ?: return
 
         try {
             val query = EntryProvider.QueryEnum.PAGE_INFO_QUERY
@@ -216,7 +214,7 @@
         if (page.hasRuntimeParam()) return null
         val context = LocalContext.current
         val route = page.buildRoute()
-        val intent = Intent(context, browseActivityClass).apply {
+        val intent = Intent(context, spaEnvironment.browseActivityClass).apply {
             putExtra(KEY_DESTINATION, route)
         }
         return {
@@ -230,7 +228,7 @@
         if (entry.hasRuntimeParam()) return null
         val context = LocalContext.current
         val route = entry.buildRoute()
-        val intent = Intent(context, browseActivityClass).apply {
+        val intent = Intent(context, spaEnvironment.browseActivityClass).apply {
             putExtra(KEY_DESTINATION, route)
         }
         return {
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/EntryProvider.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/EntryProvider.kt
index d923c1c..f0ec83b 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/EntryProvider.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/EntryProvider.kt
@@ -28,8 +28,8 @@
 import android.database.MatrixCursor
 import android.net.Uri
 import android.util.Log
-import com.android.settingslib.spa.framework.common.SettingsEntryRepository
 import com.android.settingslib.spa.framework.common.SettingsPage
+import com.android.settingslib.spa.framework.common.SpaEnvironment
 
 /**
  * The content provider to return entry related data, which can be used for search and hierarchy.
@@ -42,10 +42,9 @@
  *   $ adb shell content query --uri content://<AuthorityPath>/page_info
  *   $ adb shell content query --uri content://<AuthorityPath>/entry_info
  */
-open class EntryProvider(
-    private val entryRepository: SettingsEntryRepository,
-    private val browseActivityClass: Class<*>? = null,
-) : ContentProvider() {
+open class EntryProvider(spaEnvironment: SpaEnvironment) : ContentProvider() {
+    private val entryRepository by spaEnvironment.entryRepository
+    private val browseActivityClass = spaEnvironment.browseActivityClass
 
     /**
      * Enum to define all column names in provider.
@@ -220,7 +219,7 @@
     }
 
     private fun createBrowsePageIntent(page: SettingsPage): Intent {
-        if (context == null || browseActivityClass == null || page.hasRuntimeParam())
+        if (context == null || page.hasRuntimeParam())
             return Intent()
 
         return Intent().setComponent(ComponentName(context!!, browseActivityClass)).apply {
@@ -231,8 +230,7 @@
     private fun createBrowsePageAdbCommand(page: SettingsPage): String? {
         if (context == null || page.hasRuntimeParam()) return null
         val packageName = context!!.packageName
-        val activityName =
-            browseActivityClass?.name?.replace(packageName, "") ?: "<browse-activity-class>"
+        val activityName = browseActivityClass.name.replace(packageName, "")
         return "adb shell am start -n $packageName/$activityName" +
             " -e ${BrowseActivity.KEY_DESTINATION} ${page.buildRoute()}"
     }
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt
new file mode 100644
index 0000000..111555b
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 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.settingslib.spa.framework.common
+
+import android.app.Activity
+
+abstract class SpaEnvironment {
+    abstract val pageProviderRepository: Lazy<SettingsPageProviderRepository>
+
+    val entryRepository = lazy { SettingsEntryRepository(pageProviderRepository.value) }
+
+    abstract val browseActivityClass: Class<out Activity>
+
+    open val entryProviderAuthorities: String? = null
+
+    // TODO: add other environment setup here.
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 7927c5d..eb53ea1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -758,16 +758,23 @@
     }
 
     public boolean isBusy() {
-        synchronized (mProfileLock) {
-            for (LocalBluetoothProfile profile : mProfiles) {
-                int status = getProfileConnectionState(profile);
-                if (status == BluetoothProfile.STATE_CONNECTING
-                        || status == BluetoothProfile.STATE_DISCONNECTING) {
-                    return true;
-                }
+        for (CachedBluetoothDevice memberDevice : getMemberDevice()) {
+            if (isBusyState(memberDevice)) {
+                return true;
             }
-            return getBondState() == BluetoothDevice.BOND_BONDING;
         }
+        return isBusyState(this);
+    }
+
+    private boolean isBusyState(CachedBluetoothDevice device){
+        for (LocalBluetoothProfile profile : device.getProfiles()) {
+            int status = device.getProfileConnectionState(profile);
+            if (status == BluetoothProfile.STATE_CONNECTING
+                    || status == BluetoothProfile.STATE_DISCONNECTING) {
+                return true;
+            }
+        }
+        return device.getBondState() == BluetoothDevice.BOND_BONDING;
     }
 
     private boolean updateProfiles() {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 79e9938..315ab0a 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -1069,4 +1069,80 @@
         assertThat(mSubCachedDevice.mDevice).isEqualTo(mDevice);
         assertThat(mCachedDevice.getMemberDevice().contains(mSubCachedDevice)).isTrue();
     }
+
+    @Test
+    public void isBusy_mainDeviceIsConnecting_returnsBusy() {
+        mCachedDevice.addMemberDevice(mSubCachedDevice);
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        updateSubDeviceProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+        when(mSubDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTING);
+
+        assertThat(mCachedDevice.getMemberDevice().contains(mSubCachedDevice)).isTrue();
+        assertThat(mCachedDevice.getProfiles().contains(mA2dpProfile)).isTrue();
+        assertThat(mSubCachedDevice.getProfiles().contains(mA2dpProfile)).isTrue();
+        assertThat(mCachedDevice.isBusy()).isTrue();
+    }
+
+    @Test
+    public void isBusy_mainDeviceIsBonding_returnsBusy() {
+        mCachedDevice.addMemberDevice(mSubCachedDevice);
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        updateSubDeviceProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        when(mSubDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+
+        when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDING);
+
+        assertThat(mCachedDevice.getMemberDevice().contains(mSubCachedDevice)).isTrue();
+        assertThat(mCachedDevice.getProfiles().contains(mA2dpProfile)).isTrue();
+        assertThat(mSubCachedDevice.getProfiles().contains(mA2dpProfile)).isTrue();
+        assertThat(mCachedDevice.isBusy()).isTrue();
+    }
+
+    @Test
+    public void isBusy_memberDeviceIsConnecting_returnsBusy() {
+        mCachedDevice.addMemberDevice(mSubCachedDevice);
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        updateSubDeviceProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+        when(mSubDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+
+        updateSubDeviceProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTING);
+
+        assertThat(mCachedDevice.getMemberDevice().contains(mSubCachedDevice)).isTrue();
+        assertThat(mCachedDevice.getProfiles().contains(mA2dpProfile)).isTrue();
+        assertThat(mSubCachedDevice.getProfiles().contains(mA2dpProfile)).isTrue();
+        assertThat(mCachedDevice.isBusy()).isTrue();
+    }
+
+    @Test
+    public void isBusy_memberDeviceIsBonding_returnsBusy() {
+        mCachedDevice.addMemberDevice(mSubCachedDevice);
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        updateSubDeviceProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+
+        when(mSubDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDING);
+
+        assertThat(mCachedDevice.getMemberDevice().contains(mSubCachedDevice)).isTrue();
+        assertThat(mCachedDevice.getProfiles().contains(mA2dpProfile)).isTrue();
+        assertThat(mSubCachedDevice.getProfiles().contains(mA2dpProfile)).isTrue();
+        assertThat(mCachedDevice.isBusy()).isTrue();
+    }
+
+    @Test
+    public void isBusy_allDevicesAreNotBusy_returnsNotBusy() {
+        mCachedDevice.addMemberDevice(mSubCachedDevice);
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        updateSubDeviceProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+        when(mSubDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+
+        assertThat(mCachedDevice.getMemberDevice().contains(mSubCachedDevice)).isTrue();
+        assertThat(mCachedDevice.getProfiles().contains(mA2dpProfile)).isTrue();
+        assertThat(mSubCachedDevice.getProfiles().contains(mA2dpProfile)).isTrue();
+        assertThat(mCachedDevice.isBusy()).isFalse();
+    }
 }
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index 234ef24..aaee42f 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -1,25 +1,6 @@
 {
   // Looking for unit test presubmit configuration?
   // This currently lives in ATP config apct/system_ui/unit_test
-  "presubmit-large": [
-    {
-      "name": "PlatformScenarioTests",
-      "options": [
-        {
-            "include-filter": "android.platform.test.scenario.sysui"
-        },
-        {
-            "include-annotation": "android.platform.test.scenario.annotation.Scenario"
-        },
-        {
-            "exclude-annotation": "org.junit.Ignore"
-        },
-        {
-            "exclude-annotation": "android.platform.test.annotations.Postsubmit"
-        }
-      ]
-    }
-  ],
   "presubmit-sysui": [
     {
       "name": "PlatformScenarioTests",
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index f1e54e0..2cd564f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -149,32 +149,6 @@
                 }
             }
         }
-
-    companion object {
-        private const val SQUISHINESS_SCALE_START = 0.5
-        private const val SQUISHINESS_SCALE_FACTOR = 0.5
-        private fun getSquishinessScale(squishinessFraction: Float): Double {
-            return SQUISHINESS_SCALE_START + SQUISHINESS_SCALE_FACTOR * squishinessFraction
-        }
-    }
-
-    var squishinessFraction: Float = 1f
-        set(value) {
-            if (field == value) {
-                return
-            }
-            field = value
-
-            val scale = getSquishinessScale(field)
-            for (mediaPlayer in MediaPlayerData.players()) {
-                mediaPlayer.mediaViewHolder?.let {
-                    it.player.bottom = it.player.top + (scale * it.player.measuredHeight).toInt()
-                } ?: mediaPlayer.recommendationViewHolder?.let {
-                    it.recommendations.bottom = it.recommendations.top +
-                            (scale * it.recommendations.measuredHeight).toInt()
-                }
-            }
-        }
     private val configListener = object : ConfigurationController.ConfigurationListener {
         override fun onDensityOrFontScaleChanged() {
             // System font changes should only happen when UMO is offscreen or a flicker may occur
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt
index 59c6635..2b381a9 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt
@@ -42,7 +42,7 @@
     }
 
     private fun List<RecentTask>.sortTasks(): List<RecentTask> =
-        asReversed().sortedBy {
+        sortedBy {
             // Show normal tasks first and only then tasks with opened app selector
             it.topActivityComponent == appSelectorComponentName
         }
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/AppIconLoader.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/AppIconLoader.kt
index 0bdddfe..0927f3b 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/AppIconLoader.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/AppIconLoader.kt
@@ -17,11 +17,17 @@
 package com.android.systemui.mediaprojection.appselector.data
 
 import android.content.ComponentName
+import android.content.Context
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.ComponentInfoFlags
 import android.graphics.drawable.Drawable
+import android.os.UserHandle
+import com.android.launcher3.icons.BaseIconFactory.IconOptions
+import com.android.launcher3.icons.IconFactory
 import com.android.systemui.dagger.qualifiers.Background
+import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.withContext
-import javax.inject.Inject
 
 interface AppIconLoader {
     suspend fun loadIcon(userId: Int, component: ComponentName): Drawable?
@@ -31,11 +37,20 @@
 @Inject
 constructor(
     @Background private val backgroundDispatcher: CoroutineDispatcher,
+    private val context: Context,
+    private val packageManager: PackageManager
 ) : AppIconLoader {
 
     override suspend fun loadIcon(userId: Int, component: ComponentName): Drawable? =
         withContext(backgroundDispatcher) {
-            // TODO(b/240924731): add a blocking call to load an icon using iconloaderlib
-            null
+            IconFactory.obtain(context).use<IconFactory, Drawable?> { iconFactory ->
+                val activityInfo = packageManager
+                        .getActivityInfo(component, ComponentInfoFlags.of(0))
+                val icon = activityInfo.loadIcon(packageManager) ?: return@withContext null
+                val userHandler = UserHandle.of(userId)
+                val options = IconOptions().apply { setUser(userHandler) }
+                val badgedIcon = iconFactory.createBadgedIconBitmap(icon, options)
+                badgedIcon.newIcon(context)
+            }
         }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt
index 6d67e28..cd994b8 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt
@@ -16,11 +16,13 @@
 
 package com.android.systemui.mediaprojection.appselector.data
 
+import android.annotation.ColorInt
 import android.content.ComponentName
 
 data class RecentTask(
     val taskId: Int,
     val userId: Int,
     val topActivityComponent: ComponentName?,
-    val baseIntentComponent: ComponentName?
+    val baseIntentComponent: ComponentName?,
+    @ColorInt val colorBackground: Int?
 )
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
index 5a09435..e8b49cd 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
@@ -16,23 +16,61 @@
 
 package com.android.systemui.mediaprojection.appselector.data
 
+import android.app.ActivityManager
+import android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE
 import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.util.kotlin.getOrNull
+import com.android.wm.shell.recents.RecentTasks
+import com.android.wm.shell.util.GroupedRecentTaskInfo
+import java.util.Optional
+import javax.inject.Inject
+import kotlin.coroutines.resume
+import kotlin.coroutines.suspendCoroutine
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.withContext
-import javax.inject.Inject
+import java.util.concurrent.Executor
 
 interface RecentTaskListProvider {
+    /** Loads recent tasks, the returned task list is from the most-recent to least-recent order */
     suspend fun loadRecentTasks(): List<RecentTask>
 }
 
 class ShellRecentTaskListProvider
 @Inject
-constructor(@Background private val coroutineDispatcher: CoroutineDispatcher) :
-    RecentTaskListProvider {
+constructor(
+    @Background private val coroutineDispatcher: CoroutineDispatcher,
+    @Background private val backgroundExecutor: Executor,
+    private val recentTasks: Optional<RecentTasks>
+) : RecentTaskListProvider {
+
+    private val recents by lazy { recentTasks.getOrNull() }
 
     override suspend fun loadRecentTasks(): List<RecentTask> =
         withContext(coroutineDispatcher) {
-            // TODO(b/240924731): add blocking call to load the recents
-            emptyList()
+            val rawRecentTasks: List<GroupedRecentTaskInfo> = recents?.getTasks() ?: emptyList()
+
+            rawRecentTasks
+                .flatMap { listOfNotNull(it.taskInfo1, it.taskInfo2) }
+                .map {
+                    RecentTask(
+                        it.taskId,
+                        it.userId,
+                        it.topActivity,
+                        it.baseIntent?.component,
+                        it.taskDescription?.backgroundColor
+                    )
+                }
+        }
+
+    private suspend fun RecentTasks.getTasks(): List<GroupedRecentTaskInfo> =
+        suspendCoroutine { continuation ->
+            getRecentTasks(
+                Integer.MAX_VALUE,
+                RECENT_IGNORE_UNAVAILABLE,
+                ActivityManager.getCurrentUser(),
+                backgroundExecutor
+            ) { tasks ->
+                continuation.resume(tasks)
+            }
         }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskThumbnailLoader.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskThumbnailLoader.kt
index 4291280..47faaed 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskThumbnailLoader.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskThumbnailLoader.kt
@@ -18,6 +18,7 @@
 
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.shared.recents.model.ThumbnailData
+import com.android.systemui.shared.system.ActivityManagerWrapper
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.withContext
@@ -30,12 +31,13 @@
 @Inject
 constructor(
     @Background private val coroutineDispatcher: CoroutineDispatcher,
-) :
-    RecentTaskThumbnailLoader {
+    private val activityManager: ActivityManagerWrapper
+) : RecentTaskThumbnailLoader {
 
     override suspend fun loadThumbnail(taskId: Int): ThumbnailData? =
         withContext(coroutineDispatcher) {
-            // TODO(b/240924731): add blocking call to load a thumbnail
-             null
+            val thumbnailData =
+                activityManager.getTaskThumbnail(taskId, /* isLowResolution= */ false)
+            if (thumbnailData.thumbnail == null) null else thumbnailData
         }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 30947e8..50a10bc 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -541,10 +541,12 @@
                 if (!mImeVisible) {
                     // IME not showing, take all touches
                     info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
+                    return;
                 }
                 if (!mView.isImeRenderingNavButtons()) {
                     // IME showing but not drawing any buttons, take all touches
                     info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
+                    return;
                 }
             }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index f41b905..18bd6b7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -27,7 +27,6 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.media.MediaCarouselController;
 import com.android.systemui.media.MediaHierarchyManager;
 import com.android.systemui.media.MediaHost;
 import com.android.systemui.media.MediaHostState;
@@ -76,14 +75,13 @@
             @Named(QS_USING_MEDIA_PLAYER) boolean usingMediaPlayer,
             @Named(QS_PANEL) MediaHost mediaHost,
             QSTileRevealController.Factory qsTileRevealControllerFactory,
-            DumpManager dumpManager, MediaCarouselController mediaCarouselController,
-            MetricsLogger metricsLogger, UiEventLogger uiEventLogger,
+            DumpManager dumpManager, MetricsLogger metricsLogger, UiEventLogger uiEventLogger,
             QSLogger qsLogger, BrightnessController.Factory brightnessControllerFactory,
             BrightnessSliderController.Factory brightnessSliderFactory,
             FalsingManager falsingManager,
             StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
         super(view, qstileHost, qsCustomizerController, usingMediaPlayer, mediaHost,
-                metricsLogger, uiEventLogger, qsLogger, dumpManager, mediaCarouselController);
+                metricsLogger, uiEventLogger, qsLogger, dumpManager);
         mTunerService = tunerService;
         mQsCustomizerController = qsCustomizerController;
         mQsTileRevealControllerFactory = qsTileRevealControllerFactory;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index a5c60a4..ded466a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -32,7 +32,6 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.Dumpable;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.media.MediaCarouselController;
 import com.android.systemui.media.MediaHost;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.qs.QSTileView;
@@ -71,7 +70,6 @@
     private final UiEventLogger mUiEventLogger;
     private final QSLogger mQSLogger;
     private final DumpManager mDumpManager;
-    private final MediaCarouselController mMediaCarouselController;
     protected final ArrayList<TileRecord> mRecords = new ArrayList<>();
     protected boolean mShouldUseSplitNotificationShade;
 
@@ -133,8 +131,7 @@
             MetricsLogger metricsLogger,
             UiEventLogger uiEventLogger,
             QSLogger qsLogger,
-            DumpManager dumpManager,
-            MediaCarouselController mediaCarouselController
+            DumpManager dumpManager
     ) {
         super(view);
         mHost = host;
@@ -147,7 +144,6 @@
         mDumpManager = dumpManager;
         mShouldUseSplitNotificationShade =
                 LargeScreenUtils.shouldUseSplitNotificationShade(getResources());
-        mMediaCarouselController = mediaCarouselController;
     }
 
     @Override
@@ -165,7 +161,6 @@
 
     public void setSquishinessFraction(float squishinessFraction) {
         mView.setSquishinessFraction(squishinessFraction);
-        mMediaCarouselController.setSquishinessFraction(squishinessFraction);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
index 7ce0ad0..9739974 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
@@ -26,7 +26,6 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.R;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.media.MediaCarouselController;
 import com.android.systemui.media.MediaHierarchyManager;
 import com.android.systemui.media.MediaHost;
 import com.android.systemui.plugins.qs.QSTile;
@@ -56,10 +55,10 @@
             @Named(QS_USING_COLLAPSED_LANDSCAPE_MEDIA)
                     Provider<Boolean> usingCollapsedLandscapeMediaProvider,
             MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger,
-            DumpManager dumpManager, MediaCarouselController mediaCarouselController
+            DumpManager dumpManager
     ) {
         super(view, qsTileHost, qsCustomizerController, usingMediaPlayer, mediaHost, metricsLogger,
-                uiEventLogger, qsLogger, dumpManager, mediaCarouselController);
+                uiEventLogger, qsLogger, dumpManager);
         mUsingCollapsedLandscapeMediaProvider = usingCollapsedLandscapeMediaProvider;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index a8ad564..76f2dd1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -204,6 +204,7 @@
     protected CentralSurfaces mCentralSurfaces;
     private NotificationPanelViewController mNotificationPanelViewController;
     private BiometricUnlockController mBiometricUnlockController;
+    private boolean mCentralSurfacesRegistered;
 
     private View mNotificationContainer;
 
@@ -338,6 +339,7 @@
         mNotificationContainer = notificationContainer;
         mKeyguardMessageAreaController = mKeyguardMessageAreaFactory.create(
                 centralSurfaces.getKeyguardMessageArea());
+        mCentralSurfacesRegistered = true;
 
         registerListeners();
     }
@@ -1086,6 +1088,9 @@
     };
 
     protected void updateStates() {
+        if (!mCentralSurfacesRegistered) {
+            return;
+        }
         boolean showing = mShowing;
         boolean occluded = mOccluded;
         boolean bouncerShowing = bouncerIsShowing();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt
index e3e3b74..5ad3542 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt
@@ -30,7 +30,6 @@
 import com.android.systemui.statusbar.notification.collection.provider.OnReorderingAllowedListener
 import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider
 import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.util.animation.TransitionLayout
 import com.android.systemui.util.concurrency.DelayableExecutor
 import com.android.systemui.util.mockito.capture
 import com.android.systemui.util.mockito.eq
@@ -46,7 +45,6 @@
 import org.mockito.Mock
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.verify
-import org.mockito.Mockito.verifyNoMoreInteractions
 import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoAnnotations
 
@@ -73,10 +71,6 @@
     @Mock lateinit var dumpManager: DumpManager
     @Mock lateinit var logger: MediaUiEventLogger
     @Mock lateinit var debugLogger: MediaCarouselControllerLogger
-    @Mock lateinit var mediaViewHolder: MediaViewHolder
-    @Mock lateinit var player: TransitionLayout
-    @Mock lateinit var recommendationViewHolder: RecommendationViewHolder
-    @Mock lateinit var recommendations: TransitionLayout
     @Mock lateinit var mediaPlayer: MediaControlPanel
     @Mock lateinit var mediaViewController: MediaViewController
     @Mock lateinit var smartspaceMediaData: SmartspaceMediaData
@@ -282,46 +276,6 @@
         verify(logger).logRecommendationRemoved(eq(packageName), eq(instanceId!!))
     }
 
-    @Test
-    fun testSetSquishinessFractionForMedia_setPlayerBottom() {
-        whenever(panel.mediaViewHolder).thenReturn(mediaViewHolder)
-        whenever(mediaViewHolder.player).thenReturn(player)
-        whenever(player.measuredHeight).thenReturn(100)
-
-        val playingLocal = Triple("playing local",
-                DATA.copy(active = true, isPlaying = true,
-                        playbackLocation = MediaData.PLAYBACK_LOCAL, resumption = false),
-                4500L)
-        MediaPlayerData.addMediaPlayer(playingLocal.first, playingLocal.second, panel, clock,
-                false, debugLogger)
-
-        mediaCarouselController.squishinessFraction = 0.0f
-        verify(player).bottom = 50
-        verifyNoMoreInteractions(recommendationViewHolder)
-
-        mediaCarouselController.squishinessFraction = 0.5f
-        verify(player).bottom = 75
-        verifyNoMoreInteractions(recommendationViewHolder)
-    }
-
-    @Test
-    fun testSetSquishinessFractionForRecommendation_setPlayerBottom() {
-        whenever(panel.recommendationViewHolder).thenReturn(recommendationViewHolder)
-        whenever(recommendationViewHolder.recommendations).thenReturn(recommendations)
-        whenever(recommendations.measuredHeight).thenReturn(100)
-
-        MediaPlayerData.addMediaRecommendation(SMARTSPACE_KEY, EMPTY_SMARTSPACE_MEDIA_DATA, panel,
-                false, clock)
-
-        mediaCarouselController.squishinessFraction = 0.0f
-        verifyNoMoreInteractions(mediaViewHolder)
-        verify(recommendationViewHolder.recommendations).bottom = 50
-
-        mediaCarouselController.squishinessFraction = 0.5f
-        verifyNoMoreInteractions(mediaViewHolder)
-        verify(recommendationViewHolder.recommendations).bottom = 75
-    }
-
     fun testMediaLoaded_ScrollToActivePlayer() {
         listener.value.onMediaDataLoaded("playing local",
                 null,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
index 37b7f47..00b1f32 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
@@ -54,7 +54,7 @@
     }
 
     @Test
-    fun initMultipleRecentTasksWithoutAppSelectorTask_bindsListInReverse() {
+    fun initMultipleRecentTasksWithoutAppSelectorTask_bindsListInTheSameOrder() {
         val tasks = listOf(
             createRecentTask(taskId = 1),
             createRecentTask(taskId = 2),
@@ -66,15 +66,15 @@
 
         verify(view).bind(
             listOf(
-                createRecentTask(taskId = 3),
-                createRecentTask(taskId = 2),
                 createRecentTask(taskId = 1),
+                createRecentTask(taskId = 2),
+                createRecentTask(taskId = 3),
             )
         )
     }
 
     @Test
-    fun initRecentTasksWithAppSelectorTasks_bindsListInReverseAndAppSelectorTasksAtTheEnd() {
+    fun initRecentTasksWithAppSelectorTasks_bindsAppSelectorTasksAtTheEnd() {
         val tasks = listOf(
             createRecentTask(taskId = 1),
             createRecentTask(taskId = 2, topActivityComponent = appSelectorComponentName),
@@ -88,11 +88,11 @@
 
         verify(view).bind(
             listOf(
-                createRecentTask(taskId = 5),
-                createRecentTask(taskId = 3),
                 createRecentTask(taskId = 1),
-                createRecentTask(taskId = 4, topActivityComponent = appSelectorComponentName),
+                createRecentTask(taskId = 3),
+                createRecentTask(taskId = 5),
                 createRecentTask(taskId = 2, topActivityComponent = appSelectorComponentName),
+                createRecentTask(taskId = 4, topActivityComponent = appSelectorComponentName),
             )
         )
     }
@@ -105,7 +105,8 @@
             taskId = taskId,
             topActivityComponent = topActivityComponent,
             baseIntentComponent = ComponentName("com", "Test"),
-            userId = 0
+            userId = 0,
+            colorBackground = 0
         )
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt
new file mode 100644
index 0000000..939af16
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt
@@ -0,0 +1,112 @@
+package com.android.systemui.mediaprojection.appselector.data
+
+import android.app.ActivityManager.RecentTaskInfo
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.android.wm.shell.recents.RecentTasks
+import com.android.wm.shell.util.GroupedRecentTaskInfo
+import com.google.common.truth.Truth.assertThat
+import java.util.*
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.runBlocking
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.util.function.Consumer
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class ShellRecentTaskListProviderTest : SysuiTestCase() {
+
+    private val dispatcher = Dispatchers.Unconfined
+    private val recentTasks: RecentTasks = mock()
+    private val recentTaskListProvider =
+        ShellRecentTaskListProvider(dispatcher, Runnable::run, Optional.of(recentTasks))
+
+    @Test
+    fun loadRecentTasks_oneTask_returnsTheSameTask() {
+        givenRecentTasks(createSingleTask(taskId = 1))
+
+        val result = runBlocking { recentTaskListProvider.loadRecentTasks() }
+
+        assertThat(result).containsExactly(createRecentTask(taskId = 1))
+    }
+
+    @Test
+    fun loadRecentTasks_multipleTasks_returnsTheSameTasks() {
+        givenRecentTasks(
+            createSingleTask(taskId = 1),
+            createSingleTask(taskId = 2),
+            createSingleTask(taskId = 3),
+        )
+
+        val result = runBlocking { recentTaskListProvider.loadRecentTasks() }
+
+        assertThat(result)
+            .containsExactly(
+                createRecentTask(taskId = 1),
+                createRecentTask(taskId = 2),
+                createRecentTask(taskId = 3),
+            )
+    }
+
+    @Test
+    fun loadRecentTasks_groupedTask_returnsUngroupedTasks() {
+        givenRecentTasks(createTaskPair(taskId1 = 1, taskId2 = 2))
+
+        val result = runBlocking { recentTaskListProvider.loadRecentTasks() }
+
+        assertThat(result)
+            .containsExactly(createRecentTask(taskId = 1), createRecentTask(taskId = 2))
+    }
+
+    @Test
+    fun loadRecentTasks_mixedSingleAndGroupedTask_returnsUngroupedTasks() {
+        givenRecentTasks(
+            createSingleTask(taskId = 1),
+            createTaskPair(taskId1 = 2, taskId2 = 3),
+            createSingleTask(taskId = 4),
+            createTaskPair(taskId1 = 5, taskId2 = 6),
+        )
+
+        val result = runBlocking { recentTaskListProvider.loadRecentTasks() }
+
+        assertThat(result)
+            .containsExactly(
+                createRecentTask(taskId = 1),
+                createRecentTask(taskId = 2),
+                createRecentTask(taskId = 3),
+                createRecentTask(taskId = 4),
+                createRecentTask(taskId = 5),
+                createRecentTask(taskId = 6),
+            )
+    }
+
+    @Suppress("UNCHECKED_CAST")
+    private fun givenRecentTasks(vararg tasks: GroupedRecentTaskInfo) {
+        whenever(recentTasks.getRecentTasks(any(), any(), any(), any(), any())).thenAnswer {
+            val consumer = it.arguments.last() as Consumer<List<GroupedRecentTaskInfo>>
+            consumer.accept(tasks.toList())
+        }
+    }
+
+    private fun createRecentTask(taskId: Int): RecentTask =
+        RecentTask(
+            taskId = taskId,
+            userId = 0,
+            topActivityComponent = null,
+            baseIntentComponent = null,
+            colorBackground = null
+        )
+
+    private fun createSingleTask(taskId: Int): GroupedRecentTaskInfo =
+        GroupedRecentTaskInfo.forSingleTask(createTaskInfo(taskId))
+
+    private fun createTaskPair(taskId1: Int, taskId2: Int): GroupedRecentTaskInfo =
+        GroupedRecentTaskInfo.forSplitTasks(createTaskInfo(taskId1), createTaskInfo(taskId2), null)
+
+    private fun createTaskInfo(taskId: Int) = RecentTaskInfo().apply { this.taskId = taskId }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
index cbe1186..3cad2a0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
@@ -44,7 +44,6 @@
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.media.MediaCarouselController;
 import com.android.systemui.media.MediaHost;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.qs.QSTileView;
@@ -87,7 +86,6 @@
     @Mock
     private QSLogger mQSLogger;
     private DumpManager mDumpManager = new DumpManager();
-    private MediaCarouselController mMediaCarouselController;
     @Mock
     QSTileImpl mQSTile;
     @Mock
@@ -110,9 +108,9 @@
         protected TestableQSPanelControllerBase(QSPanel view, QSTileHost host,
                 QSCustomizerController qsCustomizerController, MediaHost mediaHost,
                 MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger,
-                DumpManager dumpManager, MediaCarouselController mediaCarouselController) {
+                DumpManager dumpManager) {
             super(view, host, qsCustomizerController, true, mediaHost, metricsLogger, uiEventLogger,
-                    qsLogger, dumpManager, mediaCarouselController);
+                    qsLogger, dumpManager);
         }
 
         @Override
@@ -146,7 +144,7 @@
 
         mController = new TestableQSPanelControllerBase(mQSPanel, mQSTileHost,
                 mQSCustomizerController, mMediaHost,
-                mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager, mMediaCarouselController);
+                mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager);
 
         mController.init();
         reset(mQSTileRevealController);
@@ -158,7 +156,7 @@
 
         QSPanelControllerBase<QSPanel> controller = new TestableQSPanelControllerBase(mQSPanel,
                 mQSTileHost, mQSCustomizerController, mMediaHost,
-                mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager, mMediaCarouselController) {
+                mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager) {
             @Override
             protected QSTileRevealController createTileRevealController() {
                 return mQSTileRevealController;
@@ -253,7 +251,7 @@
         when(mQSPanel.getDumpableTag()).thenReturn("QSPanelLandscape");
         mController = new TestableQSPanelControllerBase(mQSPanel, mQSTileHost,
                 mQSCustomizerController, mMediaHost,
-                mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager, mMediaCarouselController);
+                mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager);
         mController.init();
 
         assertThat(mController.shouldUseHorizontalLayout()).isTrue();
@@ -262,7 +260,7 @@
         when(mQSPanel.getDumpableTag()).thenReturn("QSPanelPortrait");
         mController = new TestableQSPanelControllerBase(mQSPanel, mQSTileHost,
                 mQSCustomizerController, mMediaHost,
-                mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager, mMediaCarouselController);
+                mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager);
         mController.init();
 
         assertThat(mController.shouldUseHorizontalLayout()).isFalse();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
index 98d499a..5eb9a98 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
@@ -6,7 +6,6 @@
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.media.MediaCarouselController
 import com.android.systemui.media.MediaHost
 import com.android.systemui.media.MediaHostState
 import com.android.systemui.plugins.FalsingManager
@@ -41,7 +40,6 @@
     @Mock private lateinit var qsCustomizerController: QSCustomizerController
     @Mock private lateinit var qsTileRevealControllerFactory: QSTileRevealController.Factory
     @Mock private lateinit var dumpManager: DumpManager
-    @Mock private lateinit var mediaCarouselController: MediaCarouselController
     @Mock private lateinit var metricsLogger: MetricsLogger
     @Mock private lateinit var uiEventLogger: UiEventLogger
     @Mock private lateinit var qsLogger: QSLogger
@@ -78,7 +76,6 @@
             mediaHost,
             qsTileRevealControllerFactory,
             dumpManager,
-            mediaCarouselController,
             metricsLogger,
             uiEventLogger,
             qsLogger,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
index 4af5b90..6af8e49 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
@@ -23,7 +23,6 @@
 import com.android.internal.logging.testing.UiEventLoggerFake
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.media.MediaCarouselController
 import com.android.systemui.media.MediaHost
 import com.android.systemui.media.MediaHostState
 import com.android.systemui.plugins.qs.QSTile
@@ -60,7 +59,6 @@
     @Mock private lateinit var tileLayout: TileLayout
     @Mock private lateinit var tileView: QSTileView
     @Captor private lateinit var captor: ArgumentCaptor<QSPanel.OnConfigurationChangedListener>
-    @Mock private lateinit var mediaCarouselController: MediaCarouselController
 
     private val uiEventLogger = UiEventLoggerFake()
     private val dumpManager = DumpManager()
@@ -90,8 +88,7 @@
                 metricsLogger,
                 uiEventLogger,
                 qsLogger,
-                dumpManager,
-                mediaCarouselController)
+                dumpManager)
 
         controller.init()
     }
@@ -158,8 +155,7 @@
         metricsLogger: MetricsLogger,
         uiEventLogger: UiEventLoggerFake,
         qsLogger: QSLogger,
-        dumpManager: DumpManager,
-        mediaCarouselController: MediaCarouselController
+        dumpManager: DumpManager
     ) :
         QuickQSPanelController(
             view,
@@ -171,8 +167,7 @@
             metricsLogger,
             uiEventLogger,
             qsLogger,
-            dumpManager,
-            mediaCarouselController) {
+            dumpManager) {
 
         private var rotation = RotationUtils.ROTATION_NONE
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
index c47ea9c..6ace404 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
@@ -275,10 +275,9 @@
         EditText editText = view.findViewById(R.id.remote_input_text);
         editText.setText(TEST_REPLY);
         ClipDescription description = new ClipDescription("", new String[] {"image/png"});
-        // We need to use an (arbitrary) real resource here so that an actual image gets attached.
+        // We need to use an (arbitrary) real resource here so that an actual image gets attached
         ClipData clip = new ClipData(description, new ClipData.Item(
-                Uri.parse("android.resource://com.android.systemui/"
-                        + R.drawable.default_thumbnail)));
+                Uri.parse("android.resource://android/" + android.R.drawable.btn_default)));
         ContentInfo payload =
                 new ContentInfo.Builder(clip, SOURCE_CLIPBOARD).build();
         view.setAttachment(payload);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 8d94f95..f17f8f7 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -731,7 +731,6 @@
         intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
         intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
         intentFilter.addAction(Intent.ACTION_USER_REMOVED);
-        intentFilter.addAction(Intent.ACTION_USER_PRESENT);
         intentFilter.addAction(Intent.ACTION_SETTING_RESTORED);
 
         mContext.registerReceiverAsUser(new BroadcastReceiver() {
@@ -749,14 +748,6 @@
                     unlockUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
                 } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
                     removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
-                } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
-                    // We will update when the automation service dies.
-                    synchronized (mLock) {
-                        AccessibilityUserState userState = getCurrentUserStateLocked();
-                        if (readConfigurationForUserStateLocked(userState)) {
-                            onUserStateChangedLocked(userState);
-                        }
-                    }
                 } else if (Intent.ACTION_SETTING_RESTORED.equals(action)) {
                     final String which = intent.getStringExtra(Intent.EXTRA_SETTING_NAME);
                     if (Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES.equals(which)) {
diff --git a/services/companion/java/com/android/server/companion/virtual/InputController.java b/services/companion/java/com/android/server/companion/virtual/InputController.java
index 838cbd9..ec30369b 100644
--- a/services/companion/java/com/android/server/companion/virtual/InputController.java
+++ b/services/companion/java/com/android/server/companion/virtual/InputController.java
@@ -24,7 +24,6 @@
 import android.hardware.display.DisplayManagerInternal;
 import android.hardware.input.InputDeviceIdentifier;
 import android.hardware.input.InputManager;
-import android.hardware.input.InputManagerInternal;
 import android.hardware.input.VirtualKeyEvent;
 import android.hardware.input.VirtualMouseButtonEvent;
 import android.hardware.input.VirtualMouseRelativeEvent;
@@ -42,6 +41,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.LocalServices;
+import com.android.server.input.InputManagerInternal;
 
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
diff --git a/services/core/java/com/android/server/am/BroadcastConstants.java b/services/core/java/com/android/server/am/BroadcastConstants.java
index 3efb628..f9b0dd0 100644
--- a/services/core/java/com/android/server/am/BroadcastConstants.java
+++ b/services/core/java/com/android/server/am/BroadcastConstants.java
@@ -137,6 +137,14 @@
     private static final int DEFAULT_MAX_RUNNING_ACTIVE_BROADCASTS = 16;
 
     /**
+     * For {@link BroadcastQueueModernImpl}: Maximum number of pending
+     * broadcasts to hold for a process before we ignore any delays that policy
+     * might have applied to that process.
+     */
+    public int MAX_PENDING_BROADCASTS = DEFAULT_MAX_PENDING_BROADCASTS;
+    private static final int DEFAULT_MAX_PENDING_BROADCASTS = 256;
+
+    /**
      * For {@link BroadcastQueueModernImpl}: Default delay to apply to normal
      * broadcasts, giving a chance for debouncing of rapidly changing events.
      */
@@ -217,6 +225,8 @@
                 DEFAULT_MAX_RUNNING_PROCESS_QUEUES);
         MAX_RUNNING_ACTIVE_BROADCASTS = properties.getInt("bcast_max_running_active_broadcasts",
                 DEFAULT_MAX_RUNNING_ACTIVE_BROADCASTS);
+        MAX_PENDING_BROADCASTS = properties.getInt("bcast_max_pending_broadcasts",
+                DEFAULT_MAX_PENDING_BROADCASTS);
         DELAY_NORMAL_MILLIS = properties.getLong("bcast_delay_normal_millis",
                 DEFAULT_DELAY_NORMAL_MILLIS);
         DELAY_CACHED_MILLIS = properties.getLong("bcast_delay_cached_millis",
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index 77eefb4..342d1f2 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -119,6 +119,9 @@
 
     private boolean mProcessCached;
 
+    private String mCachedToString;
+    private String mCachedToShortString;
+
     public BroadcastProcessQueue(@NonNull BroadcastConstants constants,
             @NonNull String processName, int uid) {
         this.constants = Objects.requireNonNull(constants);
@@ -327,7 +330,7 @@
     }
 
     public boolean isEmpty() {
-        return (mActive != null) && mPending.isEmpty();
+        return mPending.isEmpty();
     }
 
     public boolean isActive() {
@@ -385,6 +388,12 @@
             } else {
                 mRunnableAt = runnableAt + constants.DELAY_NORMAL_MILLIS;
             }
+
+            // If we have too many broadcasts pending, bypass any delays that
+            // might have been applied above to aid draining
+            if (mPending.size() >= constants.MAX_PENDING_BROADCASTS) {
+                mRunnableAt = runnableAt;
+            }
         } else {
             mRunnableAt = Long.MAX_VALUE;
         }
@@ -452,13 +461,19 @@
 
     @Override
     public String toString() {
-        return "BroadcastProcessQueue{"
-                + Integer.toHexString(System.identityHashCode(this))
-                + " " + processName + "/" + UserHandle.formatUid(uid) + "}";
+        if (mCachedToString == null) {
+            mCachedToString = "BroadcastProcessQueue{"
+                    + Integer.toHexString(System.identityHashCode(this))
+                    + " " + processName + "/" + UserHandle.formatUid(uid) + "}";
+        }
+        return mCachedToString;
     }
 
     public String toShortString() {
-        return processName + "/" + UserHandle.formatUid(uid);
+        if (mCachedToShortString == null) {
+            mCachedToShortString = processName + "/" + UserHandle.formatUid(uid);
+        }
+        return mCachedToShortString;
     }
 
     public void dumpLocked(@NonNull IndentingPrintWriter pw) {
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 972a1ce..b46a2b2 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -114,6 +114,9 @@
     /**
      * Signal from OS internals that the given process has just been actively
      * attached, and is ready to begin receiving broadcasts.
+     *
+     * @return if the queue performed an action on the given process, such as
+     *         dispatching a pending broadcast
      */
     @GuardedBy("mService")
     public abstract boolean onApplicationAttachedLocked(@NonNull ProcessRecord app);
@@ -123,7 +126,7 @@
      * an attempted start and attachment.
      */
     @GuardedBy("mService")
-    public abstract boolean onApplicationTimeoutLocked(@NonNull ProcessRecord app);
+    public abstract void onApplicationTimeoutLocked(@NonNull ProcessRecord app);
 
     /**
      * Signal from OS internals that the given process, which had already been
@@ -131,14 +134,14 @@
      * not responding.
      */
     @GuardedBy("mService")
-    public abstract boolean onApplicationProblemLocked(@NonNull ProcessRecord app);
+    public abstract void onApplicationProblemLocked(@NonNull ProcessRecord app);
 
     /**
      * Signal from OS internals that the given process has been killed, and is
      * no longer actively running.
      */
     @GuardedBy("mService")
-    public abstract boolean onApplicationCleanupLocked(@NonNull ProcessRecord app);
+    public abstract void onApplicationCleanupLocked(@NonNull ProcessRecord app);
 
     /**
      * Signal from OS internals that the given package (or some subset of that
diff --git a/services/core/java/com/android/server/am/BroadcastQueueImpl.java b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
index 28bd9c3..1671185 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
@@ -426,16 +426,16 @@
         }
     }
 
-    public boolean onApplicationTimeoutLocked(ProcessRecord app) {
-        return skipCurrentOrPendingReceiverLocked(app);
+    public void onApplicationTimeoutLocked(ProcessRecord app) {
+        skipCurrentOrPendingReceiverLocked(app);
     }
 
-    public boolean onApplicationProblemLocked(ProcessRecord app) {
-        return skipCurrentOrPendingReceiverLocked(app);
+    public void onApplicationProblemLocked(ProcessRecord app) {
+        skipCurrentOrPendingReceiverLocked(app);
     }
 
-    public boolean onApplicationCleanupLocked(ProcessRecord app) {
-        return skipCurrentOrPendingReceiverLocked(app);
+    public void onApplicationCleanupLocked(ProcessRecord app) {
+        skipCurrentOrPendingReceiverLocked(app);
     }
 
     public boolean sendPendingBroadcastsLocked(ProcessRecord app) {
@@ -732,9 +732,10 @@
                 } catch (RemoteException ex) {
                     // Failed to call into the process. It's either dying or wedged. Kill it gently.
                     synchronized (mService) {
-                        Slog.w(TAG, "Can't deliver broadcast to " + app.processName
-                                + " (pid " + app.getPid() + "). Crashing it.");
-                        app.scheduleCrashLocked("can't deliver broadcast",
+                        final String msg = "Failed to schedule " + intent + " to " + receiver
+                                + " via " + app + ": " + ex;
+                        Slog.w(TAG, msg);
+                        app.scheduleCrashLocked(msg,
                                 CannotDeliverBroadcastException.TYPE_ID, /* extras=*/ null);
                     }
                     throw ex;
@@ -814,7 +815,11 @@
         try {
             if (DEBUG_BROADCAST_LIGHT) Slog.i(TAG_BROADCAST,
                     "Delivering to " + filter + " : " + r);
-            if (filter.receiverList.app != null && filter.receiverList.app.isInFullBackup()) {
+            final boolean isInFullBackup = (filter.receiverList.app != null)
+                    && filter.receiverList.app.isInFullBackup();
+            final boolean isKilled = (filter.receiverList.app != null)
+                    && filter.receiverList.app.isKilled();
+            if (isInFullBackup || isKilled) {
                 // Skip delivery if full backup in progress
                 // If it's an ordered broadcast, we need to continue to the next receiver.
                 if (ordered) {
@@ -1379,8 +1384,11 @@
                 processCurBroadcastLocked(r, app);
                 return;
             } catch (RemoteException e) {
-                Slog.w(TAG, "Exception when sending broadcast to "
-                      + r.curComponent, e);
+                final String msg = "Failed to schedule " + r.intent + " to " + info
+                        + " via " + app + ": " + e;
+                Slog.w(TAG, msg);
+                app.scheduleCrashLocked(msg,
+                        CannotDeliverBroadcastException.TYPE_ID, /* extras=*/ null);
             } catch (RuntimeException e) {
                 Slog.wtf(TAG, "Failed sending broadcast to "
                         + r.curComponent + " with " + r.intent, e);
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index a36a9f6..7c236ff 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -290,7 +290,7 @@
         }
 
         // If app isn't running, and there's nothing in the queue, clean up
-        if (queue.isEmpty() && !queue.isProcessWarm()) {
+        if (queue.isEmpty() && !queue.isActive() && !queue.isProcessWarm()) {
             removeProcessQueue(queue.processName, queue.uid);
         }
     }
@@ -420,18 +420,17 @@
     }
 
     @Override
-    public boolean onApplicationTimeoutLocked(@NonNull ProcessRecord app) {
-        return onApplicationCleanupLocked(app);
+    public void onApplicationTimeoutLocked(@NonNull ProcessRecord app) {
+        onApplicationCleanupLocked(app);
     }
 
     @Override
-    public boolean onApplicationProblemLocked(@NonNull ProcessRecord app) {
-        return onApplicationCleanupLocked(app);
+    public void onApplicationProblemLocked(@NonNull ProcessRecord app) {
+        onApplicationCleanupLocked(app);
     }
 
     @Override
-    public boolean onApplicationCleanupLocked(@NonNull ProcessRecord app) {
-        boolean didSomething = false;
+    public void onApplicationCleanupLocked(@NonNull ProcessRecord app) {
         if ((mRunningColdStart != null) && (mRunningColdStart.app == app)) {
             // We've been waiting for this app to cold start, and it had
             // trouble; clear the slot and fail delivery below
@@ -439,7 +438,6 @@
 
             // We might be willing to kick off another cold start
             enqueueUpdateRunningList();
-            didSomething = true;
         }
 
         final BroadcastProcessQueue queue = getProcessQueue(app);
@@ -449,16 +447,19 @@
             // If queue was running a broadcast, fail it
             if (queue.isActive()) {
                 finishReceiverLocked(queue, BroadcastRecord.DELIVERY_FAILURE);
-                didSomething = true;
             }
 
+            // Skip any pending registered receivers, since the old process
+            // would never be around to receive them
+            queue.removeMatchingBroadcasts((r, i) -> {
+                return (r.receivers.get(i) instanceof BroadcastFilter);
+            }, mBroadcastConsumerSkip);
+
             // If queue has nothing else pending, consider cleaning it
             if (queue.isEmpty()) {
                 updateRunnableList(queue);
             }
         }
-
-        return didSomething;
     }
 
     @Override
@@ -515,6 +516,13 @@
         final int index = queue.getActiveIndex();
         final Object receiver = r.receivers.get(index);
 
+        // Ignore registered receivers from a previous PID
+        if (receiver instanceof BroadcastFilter) {
+            mRunningColdStart = null;
+            finishReceiverLocked(queue, BroadcastRecord.DELIVERY_SKIPPED);
+            return;
+        }
+
         final ApplicationInfo info = ((ResolveInfo) receiver).activityInfo.applicationInfo;
         final ComponentName component = ((ResolveInfo) receiver).activityInfo.getComponentName();
 
@@ -536,6 +544,7 @@
         } else {
             mRunningColdStart = null;
             finishReceiverLocked(queue, BroadcastRecord.DELIVERY_FAILURE);
+            return;
         }
     }
 
@@ -563,7 +572,7 @@
             return;
         }
 
-        // Consider additional cases where we'd want fo finish immediately
+        // Consider additional cases where we'd want to finish immediately
         if (app.isInFullBackup()) {
             finishReceiverLocked(queue, BroadcastRecord.DELIVERY_SKIPPED);
             return;
@@ -578,7 +587,14 @@
             return;
         }
 
-        if (!r.timeoutExempt) {
+        // Ignore registered receivers from a previous PID
+        if ((receiver instanceof BroadcastFilter)
+                && ((BroadcastFilter) receiver).receiverList.pid != app.getPid()) {
+            finishReceiverLocked(queue, BroadcastRecord.DELIVERY_SKIPPED);
+            return;
+        }
+
+        if (mService.mProcessesReady && !r.timeoutExempt) {
             final long timeout = r.isForeground() ? mFgConstants.TIMEOUT : mBgConstants.TIMEOUT;
             mLocalHandler.sendMessageDelayed(
                     Message.obtain(mLocalHandler, MSG_DELIVERY_TIMEOUT, queue), timeout);
@@ -604,7 +620,7 @@
         }
 
         if (DEBUG_BROADCAST) logv("Scheduling " + r + " to warm " + app);
-        setDeliveryState(queue, r, index, receiver, BroadcastRecord.DELIVERY_SCHEDULED);
+        setDeliveryState(queue, app, r, index, receiver, BroadcastRecord.DELIVERY_SCHEDULED);
 
         final IApplicationThread thread = app.getThread();
         if (thread != null) {
@@ -628,8 +644,12 @@
                             app.mState.getReportedProcState());
                 }
             } catch (RemoteException e) {
+                final String msg = "Failed to schedule " + r + " to " + receiver
+                        + " via " + app + ": " + e;
+                Slog.w(TAG, msg);
+                app.scheduleCrashLocked(msg, CannotDeliverBroadcastException.TYPE_ID, null);
+                app.setKilled(true);
                 finishReceiverLocked(queue, BroadcastRecord.DELIVERY_FAILURE);
-                app.scheduleCrashLocked(TAG, CannotDeliverBroadcastException.TYPE_ID, null);
             }
         } else {
             finishReceiverLocked(queue, BroadcastRecord.DELIVERY_FAILURE);
@@ -652,7 +672,9 @@
                         r.resultCode, r.resultData, r.resultExtras, false, r.initialSticky,
                         r.userId, app.mState.getReportedProcState());
             } catch (RemoteException e) {
-                app.scheduleCrashLocked(TAG, CannotDeliverBroadcastException.TYPE_ID, null);
+                final String msg = "Failed to schedule result of " + r + " via " + app + ": " + e;
+                Slog.w(TAG, msg);
+                app.scheduleCrashLocked(msg, CannotDeliverBroadcastException.TYPE_ID, null);
             }
         }
     }
@@ -674,7 +696,8 @@
         // receivers as skipped
         if (r.ordered && r.resultAbort) {
             for (int i = r.finishedCount + 1; i < r.receivers.size(); i++) {
-                setDeliveryState(null, r, i, r.receivers.get(i), BroadcastRecord.DELIVERY_SKIPPED);
+                setDeliveryState(null, null, r, i, r.receivers.get(i),
+                        BroadcastRecord.DELIVERY_SKIPPED);
             }
         }
 
@@ -690,9 +713,10 @@
         final int index = queue.getActiveIndex();
         final Object receiver = r.receivers.get(index);
 
-        setDeliveryState(queue, r, index, receiver, deliveryState);
+        setDeliveryState(queue, app, r, index, receiver, deliveryState);
 
         if (deliveryState == BroadcastRecord.DELIVERY_TIMEOUT) {
+            r.anrCount++;
             if (app != null && !app.isDebugging()) {
                 mService.appNotResponding(queue.app, TimeoutRecord
                         .forBroadcastReceiver("Broadcast of " + r.toShortString()));
@@ -704,7 +728,7 @@
         // Even if we have more broadcasts, if we've made reasonable progress
         // and someone else is waiting, retire ourselves to avoid starvation
         final boolean shouldRetire = (mRunnableHead != null)
-                && (queue.getActiveCountSinceIdle() > mConstants.MAX_RUNNING_ACTIVE_BROADCASTS);
+                && (queue.getActiveCountSinceIdle() >= mConstants.MAX_RUNNING_ACTIVE_BROADCASTS);
 
         if (queue.isRunnable() && queue.isProcessWarm() && !shouldRetire) {
             // We're on a roll; move onto the next broadcast for this process
@@ -733,17 +757,22 @@
      * bookkeeping related to ordered broadcasts.
      */
     private void setDeliveryState(@Nullable BroadcastProcessQueue queue,
-            @NonNull BroadcastRecord r, int index, @NonNull Object receiver,
-            @DeliveryState int newDeliveryState) {
+            @Nullable ProcessRecord app, @NonNull BroadcastRecord r, int index,
+            @NonNull Object receiver, @DeliveryState int newDeliveryState) {
         final int oldDeliveryState = getDeliveryState(r, index);
 
         if (newDeliveryState != BroadcastRecord.DELIVERY_DELIVERED) {
-            Slog.w(TAG, "Delivery state of " + r + " to " + receiver + " changed from "
+            Slog.w(TAG, "Delivery state of " + r + " to " + receiver
+                    + " via " + app + " changed from "
                     + deliveryStateToString(oldDeliveryState) + " to "
                     + deliveryStateToString(newDeliveryState));
         }
 
-        r.setDeliveryState(index, newDeliveryState);
+        // Only apply state when we haven't already reached a terminal state;
+        // this is how we ignore racing timeout messages
+        if (!isDeliveryStateTerminal(oldDeliveryState)) {
+            r.setDeliveryState(index, newDeliveryState);
+        }
 
         // Emit any relevant tracing results when we're changing the delivery
         // state as part of running from a queue
@@ -837,7 +866,7 @@
      * of it matching a predicate.
      */
     private final BroadcastConsumer mBroadcastConsumerSkip = (r, i) -> {
-        setDeliveryState(null, r, i, r.receivers.get(i), BroadcastRecord.DELIVERY_SKIPPED);
+        setDeliveryState(null, null, r, i, r.receivers.get(i), BroadcastRecord.DELIVERY_SKIPPED);
     };
 
     private boolean skipMatchingBroadcasts(
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index ae7f2a5..33f74f3 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -128,6 +128,9 @@
     @Nullable
     final BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver;
 
+    String cachedToString;
+    String cachedToShortString;
+
     static final int IDLE = 0;
     static final int APP_RECEIVE = 1;
     static final int CALL_IN_RECEIVE = 2;
@@ -700,21 +703,27 @@
 
     @Override
     public String toString() {
-        String label = intent.getAction();
-        if (label == null) {
-            label = intent.toString();
+        if (cachedToString == null) {
+            String label = intent.getAction();
+            if (label == null) {
+                label = intent.toString();
+            }
+            cachedToString = "BroadcastRecord{"
+                + Integer.toHexString(System.identityHashCode(this))
+                + " u" + userId + " " + label + "}";
         }
-        return "BroadcastRecord{"
-            + Integer.toHexString(System.identityHashCode(this))
-            + " u" + userId + " " + label + "}";
+        return cachedToString;
     }
 
     public String toShortString() {
-        String label = intent.getAction();
-        if (label == null) {
-            label = intent.toString();
+        if (cachedToShortString == null) {
+            String label = intent.getAction();
+            if (label == null) {
+                label = intent.toString();
+            }
+            cachedToShortString = label + "/u" + userId;
         }
-        return label + "/u" + userId;
+        return cachedToShortString;
     }
 
     public void dumpDebug(ProtoOutputStream proto, long fieldId) {
diff --git a/services/core/java/com/android/server/am/ProcessProfileRecord.java b/services/core/java/com/android/server/am/ProcessProfileRecord.java
index 7369e8f..4c15308 100644
--- a/services/core/java/com/android/server/am/ProcessProfileRecord.java
+++ b/services/core/java/com/android/server/am/ProcessProfileRecord.java
@@ -17,7 +17,6 @@
 package com.android.server.am;
 
 import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
-import static android.app.ActivityManager.processStateAmToProto;
 
 import android.annotation.IntDef;
 import android.app.IApplicationThread;
@@ -318,12 +317,6 @@
                             origBase.setState(ProcessStats.STATE_NOTHING,
                                     tracker.getMemFactorLocked(), SystemClock.uptimeMillis(),
                                     pkgList.getPackageListLocked());
-                            pkgList.forEachPackage((pkgName, holder) ->
-                                    FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
-                                        mApp.uid, mApp.processName, pkgName,
-                                        processStateAmToProto(ProcessStats.STATE_NOTHING),
-                                        holder.appVersion)
-                            );
                         }
                         origBase.makeInactive();
                     }
@@ -362,12 +355,6 @@
                         origBase.setState(ProcessStats.STATE_NOTHING,
                                 tracker.getMemFactorLocked(), SystemClock.uptimeMillis(),
                                 pkgList.getPackageListLocked());
-                        pkgList.forEachPackage((pkgName, holder) ->
-                                FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
-                                    mApp.uid, mApp.processName, pkgName,
-                                    processStateAmToProto(ProcessStats.STATE_NOTHING),
-                                    holder.appVersion)
-                        );
                     }
                     origBase.makeInactive();
                     setBaseProcessTracker(null);
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 07b6fcd..482e6a7 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -54,7 +54,6 @@
 import com.android.internal.app.procstats.ProcessState;
 import com.android.internal.app.procstats.ProcessStats;
 import com.android.internal.os.Zygote;
-import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.wm.WindowProcessController;
 import com.android.server.wm.WindowProcessListener;
 
@@ -1212,12 +1211,6 @@
                     long now = SystemClock.uptimeMillis();
                     baseProcessTracker.setState(ProcessStats.STATE_NOTHING,
                             tracker.getMemFactorLocked(), now, mPkgList.getPackageListLocked());
-                    mPkgList.forEachPackage((pkgName, holder) ->
-                            FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
-                                uid, processName, pkgName,
-                                ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING),
-                                holder.appVersion)
-                    );
                     if (numOfPkgs != 1) {
                         mPkgList.forEachPackageProcessStats(holder -> {
                             if (holder.state != null && holder.state != baseProcessTracker) {
diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java
index ef13778..d2ef479 100644
--- a/services/core/java/com/android/server/am/ProcessStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessStateRecord.java
@@ -35,7 +35,6 @@
 
 import com.android.internal.annotations.CompositeRWLock;
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.am.PlatformCompatCache.CachedCompatChangeId;
 
 import java.io.PrintWriter;
@@ -599,12 +598,6 @@
     @GuardedBy({"mService", "mProcLock"})
     void setReportedProcState(int repProcState) {
         mRepProcState = repProcState;
-        mApp.getPkgList().forEachPackage((pkgName, holder) ->
-                FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
-                    mApp.uid, mApp.processName, pkgName,
-                    ActivityManager.processStateAmToProto(mRepProcState),
-                    holder.appVersion)
-        );
         mApp.getWindowProcessController().setReportedProcState(repProcState);
     }
 
@@ -620,12 +613,6 @@
                 mRepProcState = newState;
                 setCurProcState(newState);
                 setCurRawProcState(newState);
-                mApp.getPkgList().forEachPackage((pkgName, holder) ->
-                        FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
-                            mApp.uid, mApp.processName, pkgName,
-                            ActivityManager.processStateAmToProto(mRepProcState),
-                            holder.appVersion)
-                );
             }
         }
     }
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 403ad9b..bc650ad 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -4817,7 +4817,7 @@
         pw.println("    Only output the watcher sections.");
         pw.println("  --history");
         pw.println("    Only output history.");
-        pw.println("  --uid-state-change-logs");
+        pw.println("  --uid-state-changes");
         pw.println("    Include logs about uid state changes.");
     }
 
@@ -5039,7 +5039,7 @@
                 } else if (arg.length() > 0 && arg.charAt(0) == '-') {
                     pw.println("Unknown option: " + arg);
                     return;
-                } else if ("--uid-state-change-logs".equals(arg)) {
+                } else if ("--uid-state-changes".equals(arg)) {
                     dumpUidStateChangeLogs = true;
                 } else {
                     pw.println("Unknown command: " + arg);
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 2cde526..e851f03 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -80,7 +80,6 @@
 import android.hardware.display.VirtualDisplayConfig;
 import android.hardware.display.WifiDisplayStatus;
 import android.hardware.graphics.common.DisplayDecorationSupport;
-import android.hardware.input.InputManagerInternal;
 import android.media.projection.IMediaProjection;
 import android.media.projection.IMediaProjectionManager;
 import android.net.Uri;
@@ -134,6 +133,7 @@
 import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
 import com.android.server.display.DisplayDeviceConfig.SensorData;
 import com.android.server.display.utils.SensorUtils;
+import com.android.server.input.InputManagerInternal;
 import com.android.server.wm.SurfaceAnimationThread;
 import com.android.server.wm.WindowManagerInternal;
 
diff --git a/services/core/java/com/android/server/display/TEST_MAPPING b/services/core/java/com/android/server/display/TEST_MAPPING
index 66ec5c4..57c2e01 100644
--- a/services/core/java/com/android/server/display/TEST_MAPPING
+++ b/services/core/java/com/android/server/display/TEST_MAPPING
@@ -7,6 +7,15 @@
                 {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
                 {"exclude-annotation": "androidx.test.filters.FlakyTest"}
             ]
+        },
+        {
+            "name": "FrameworksServicesTests",
+            "options": [
+                {"include-filter": "com.android.server.display"},
+                {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
+                {"exclude-annotation": "androidx.test.filters.FlakyTest"},
+                {"exclude-annotation": "org.junit.Ignore"}
+            ]
         }
     ]
 }
\ No newline at end of file
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index fee1f5c..e1b18f2 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -38,7 +38,6 @@
 import android.content.pm.ServiceInfo;
 import android.database.ContentObserver;
 import android.hardware.display.AmbientDisplayConfiguration;
-import android.hardware.input.InputManagerInternal;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Handler;
@@ -66,6 +65,7 @@
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
+import com.android.server.input.InputManagerInternal;
 import com.android.server.wm.ActivityInterceptorCallback;
 import com.android.server.wm.ActivityTaskManagerInternal;
 
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index e03a46c..3ee3503 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -266,10 +266,6 @@
     // Make sure HdmiCecConfig is instantiated and the XMLs are read.
     private HdmiCecConfig mHdmiCecConfig;
 
-    // Last return value of getPhysicalAddress(). Only updated on calls of getPhysicalAddress().
-    // Does not represent the current physical address at all times. Not to be used as a cache.
-    private int mPhysicalAddress = Constants.INVALID_PHYSICAL_ADDRESS;
-
     /**
      * Interface to report send result.
      */
@@ -2082,15 +2078,9 @@
         @Override
         public int getPhysicalAddress() {
             initBinderCall();
-            runOnServiceThread(new Runnable() {
-                @Override
-                public void run() {
-                    synchronized (mLock) {
-                        mPhysicalAddress = mHdmiCecNetwork.getPhysicalAddress();
-                    }
-                }
-            });
-            return mPhysicalAddress;
+            synchronized (mLock) {
+                return mHdmiCecNetwork.getPhysicalAddress();
+            }
         }
 
         @Override
diff --git a/core/java/android/hardware/input/InputManagerInternal.java b/services/core/java/com/android/server/input/InputManagerInternal.java
similarity index 98%
rename from core/java/android/hardware/input/InputManagerInternal.java
rename to services/core/java/com/android/server/input/InputManagerInternal.java
index fc6bc55..36099b0 100644
--- a/core/java/android/hardware/input/InputManagerInternal.java
+++ b/services/core/java/com/android/server/input/InputManagerInternal.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.hardware.input;
+package com.android.server.input;
 
 import android.annotation.NonNull;
 import android.graphics.PointF;
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 05c4f77..71feb95 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -55,8 +55,6 @@
 import android.hardware.input.ITabletModeChangedListener;
 import android.hardware.input.InputDeviceIdentifier;
 import android.hardware.input.InputManager;
-import android.hardware.input.InputManagerInternal;
-import android.hardware.input.InputManagerInternal.LidSwitchCallback;
 import android.hardware.input.InputSensorInfo;
 import android.hardware.input.KeyboardLayout;
 import android.hardware.input.TouchCalibration;
@@ -123,6 +121,7 @@
 import com.android.server.DisplayThread;
 import com.android.server.LocalServices;
 import com.android.server.Watchdog;
+import com.android.server.input.InputManagerInternal.LidSwitchCallback;
 import com.android.server.policy.WindowManagerPolicy;
 
 import libcore.io.IoUtils;
diff --git a/services/core/java/com/android/server/inputmethod/HandwritingModeController.java b/services/core/java/com/android/server/inputmethod/HandwritingModeController.java
index f89b6ae..a4830be 100644
--- a/services/core/java/com/android/server/inputmethod/HandwritingModeController.java
+++ b/services/core/java/com/android/server/inputmethod/HandwritingModeController.java
@@ -21,7 +21,6 @@
 import android.annotation.AnyThread;
 import android.annotation.Nullable;
 import android.annotation.UiThread;
-import android.hardware.input.InputManagerInternal;
 import android.os.IBinder;
 import android.os.Looper;
 import android.util.Slog;
@@ -35,6 +34,7 @@
 import android.view.SurfaceControl;
 
 import com.android.server.LocalServices;
+import com.android.server.input.InputManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
 
 import java.util.ArrayList;
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index ed17b9ca..4571546 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -92,7 +92,6 @@
 import android.graphics.Matrix;
 import android.hardware.display.DisplayManagerInternal;
 import android.hardware.input.InputManager;
-import android.hardware.input.InputManagerInternal;
 import android.inputmethodservice.InputMethodService;
 import android.media.AudioManagerInternal;
 import android.net.Uri;
@@ -188,6 +187,7 @@
 import com.android.server.ServiceThread;
 import com.android.server.SystemServerInitThreadPool;
 import com.android.server.SystemService;
+import com.android.server.input.InputManagerInternal;
 import com.android.server.inputmethod.InputMethodManagerInternal.InputMethodListListener;
 import com.android.server.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
 import com.android.server.inputmethod.InputMethodUtils.InputMethodSettings;
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 82b5c11..cc8761d 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -621,10 +621,9 @@
         }
     }
 
-    //TODO(b/136703681): Review this is handling multi-user properly.
-    void switchUser() {
+    // TODO(b/136703681): Review this is handling multi-user properly.
+    void switchUser(int userId) {
         synchronized (mLock) {
-            int userId = ActivityManager.getCurrentUser();
             if (mCurrentUserId != userId) {
                 final int oldUserId = mCurrentUserId;
                 mCurrentUserId = userId; // do this first
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index 4f0da795..d7d0b42 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -17,7 +17,9 @@
 package com.android.server.media;
 
 import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
 import android.app.ActivityManager;
+import android.app.UserSwitchObserver;
 import android.bluetooth.BluetoothA2dp;
 import android.bluetooth.BluetoothDevice;
 import android.content.BroadcastReceiver;
@@ -146,18 +148,27 @@
         context.registerReceiverAsUser(mReceiver, UserHandle.ALL, intentFilter, null, null);
     }
 
-    public void systemRunning() {
-        IntentFilter filter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
-        mContext.registerReceiver(new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                if (intent.getAction().equals(Intent.ACTION_USER_SWITCHED)) {
-                    switchUser();
-                }
-            }
-        }, filter);
-
-        switchUser();
+    /**
+     * Initializes the MediaRouter service.
+     *
+     * @throws RemoteException If an error occurs while registering the {@link UserSwitchObserver}.
+     */
+    @RequiresPermission(
+            anyOf = {
+                "android.permission.INTERACT_ACROSS_USERS",
+                "android.permission.INTERACT_ACROSS_USERS_FULL"
+            })
+    public void systemRunning() throws RemoteException {
+        ActivityManager.getService()
+                .registerUserSwitchObserver(
+                        new UserSwitchObserver() {
+                            @Override
+                            public void onUserSwitchComplete(int newUserId) {
+                                switchUser(newUserId);
+                            }
+                        },
+                        TAG);
+        switchUser(ActivityManager.getCurrentUser());
     }
 
     @Override
@@ -634,9 +645,8 @@
         }
     }
 
-    void switchUser() {
+    void switchUser(int userId) {
         synchronized (mLock) {
-            int userId = ActivityManager.getCurrentUser();
             if (mCurrentUserId != userId) {
                 final int oldUserId = mCurrentUserId;
                 mCurrentUserId = userId; // do this first
@@ -653,7 +663,7 @@
                 }
             }
         }
-        mService2.switchUser();
+        mService2.switchUser(userId);
     }
 
     void clientDied(ClientRecord clientRecord) {
diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java
index 2b8a196..cd074c0 100644
--- a/services/core/java/com/android/server/pm/DexOptHelper.java
+++ b/services/core/java/com/android/server/pm/DexOptHelper.java
@@ -385,6 +385,11 @@
         } else if (snapshot.isInstantApp(options.getPackageName(), UserHandle.getCallingUserId())) {
             return false;
         }
+        var pkg = snapshot.getPackage(options.getPackageName());
+        if (pkg != null && pkg.isApex()) {
+            // skip APEX
+            return true;
+        }
 
         if (options.isDexoptOnlySecondaryDex()) {
             return mPm.getDexManager().dexoptSecondaryDex(options);
@@ -427,6 +432,10 @@
                 // Package could not be found. Report failure.
                 return PackageDexOptimizer.DEX_OPT_FAILED;
             }
+            if (p.isApex()) {
+                // APEX needs no dexopt
+                return PackageDexOptimizer.DEX_OPT_SKIPPED;
+            }
             mPm.getPackageUsage().maybeWriteAsync(mPm.mSettings.getPackagesLocked());
             mPm.mCompilerStats.maybeWriteAsync();
         }
@@ -498,6 +507,9 @@
         if (packageState == null || pkg == null) {
             throw new IllegalArgumentException("Unknown package: " + packageName);
         }
+        if (pkg.isApex()) {
+            throw new IllegalArgumentException("Can't dexopt APEX package: " + packageName);
+        }
 
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
 
diff --git a/services/core/java/com/android/server/pm/InstallSource.java b/services/core/java/com/android/server/pm/InstallSource.java
index 404285c..e5f7f71 100644
--- a/services/core/java/com/android/server/pm/InstallSource.java
+++ b/services/core/java/com/android/server/pm/InstallSource.java
@@ -131,7 +131,8 @@
             @Nullable PackageSignatures initiatingPackageSignatures) {
         if (initiatingPackageName == null && originatingPackageName == null
                 && installerPackageName == null && initiatingPackageSignatures == null
-                && !isInitiatingPackageUninstalled) {
+                && !isInitiatingPackageUninstalled
+                && packageSource == PackageInstaller.PACKAGE_SOURCE_UNSPECIFIED) {
             return isOrphaned ? EMPTY_ORPHANED : EMPTY;
         }
         return new InstallSource(initiatingPackageName, originatingPackageName,
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 3fb4066..178d0ea 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -189,6 +189,11 @@
             return false;
         }
 
+        // We do not dexopt APEX packages.
+        if (pkg.isApex()) {
+            return false;
+        }
+
         // We do not dexopt unused packages.
         // It's possible for this to be called before app hibernation service is ready due to
         // an OTA dexopt. In this case, we ignore the hibernation check here. This is fine since
diff --git a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
index 9b7d19a..f8fcaff 100644
--- a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
+++ b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
@@ -26,7 +26,6 @@
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
-import android.hardware.input.InputManagerInternal;
 import android.os.Environment;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -39,6 +38,7 @@
 import com.android.server.LocalServices;
 import com.android.server.devicestate.DeviceState;
 import com.android.server.devicestate.DeviceStateProvider;
+import com.android.server.input.InputManagerInternal;
 import com.android.server.policy.devicestate.config.Conditions;
 import com.android.server.policy.devicestate.config.DeviceStateConfig;
 import com.android.server.policy.devicestate.config.Flags;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 02d7074..07f5bcf 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -121,7 +121,6 @@
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiPlaybackClient;
 import android.hardware.hdmi.HdmiPlaybackClient.OneTouchPlayCallback;
-import android.hardware.input.InputManagerInternal;
 import android.media.AudioManager;
 import android.media.AudioManagerInternal;
 import android.media.AudioSystem;
@@ -204,6 +203,7 @@
 import com.android.server.GestureLauncherService;
 import com.android.server.LocalServices;
 import com.android.server.SystemServiceManager;
+import com.android.server.input.InputManagerInternal;
 import com.android.server.inputmethod.InputMethodManagerInternal;
 import com.android.server.policy.KeyCombinationManager.TwoKeysCombinationRule;
 import com.android.server.policy.keyguard.KeyguardServiceDelegate;
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 9336be5..69fb22c 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -27,7 +27,6 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.hardware.display.DisplayManagerInternal;
-import android.hardware.input.InputManagerInternal;
 import android.media.AudioManager;
 import android.media.Ringtone;
 import android.media.RingtoneManager;
@@ -62,6 +61,7 @@
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.EventLogTags;
 import com.android.server.LocalServices;
+import com.android.server.input.InputManagerInternal;
 import com.android.server.inputmethod.InputMethodManagerInternal;
 import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.statusbar.StatusBarManagerInternal;
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index d8d75ed..9ed77fc 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1354,10 +1354,6 @@
         return true;
     }
 
-    void setAppTimeTracker(AppTimeTracker att) {
-        appTimeTracker = att;
-    }
-
     /** Update the saved state of an activity. */
     void setSavedState(@Nullable Bundle savedState) {
         mIcicle = savedState;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 5bddae6..b473700 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -139,7 +139,6 @@
 import com.android.internal.content.ReferrerIntent;
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.function.pooled.PooledConsumer;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.LocalServices;
 import com.android.server.am.ActivityManagerService;
@@ -1578,17 +1577,12 @@
         if (rootTask.getWindowingMode() == WINDOWING_MODE_PINNED) {
             removePinnedRootTaskInSurfaceTransaction(rootTask);
         } else {
-            final PooledConsumer c = PooledLambda.obtainConsumer(
-                    ActivityTaskSupervisor::processRemoveTask, this, PooledLambda.__(Task.class));
-            rootTask.forAllLeafTasks(c, true /* traverseTopToBottom */);
-            c.recycle();
+            rootTask.forAllLeafTasks(task -> {
+                removeTask(task, true /* killProcess */, REMOVE_FROM_RECENTS, "remove-root-task");
+            }, true /* traverseTopToBottom */);
         }
     }
 
-    private void processRemoveTask(Task task) {
-        removeTask(task, true /* killProcess */, REMOVE_FROM_RECENTS, "remove-root-task");
-    }
-
     /**
      * Removes the root task associated with the given {@param task}. If the {@param task} is the
      * pinned task, then its child tasks are not explicitly removed when the root task is
@@ -2268,23 +2262,17 @@
     }
 
     void scheduleUpdateMultiWindowMode(Task task) {
-        final PooledConsumer c = PooledLambda.obtainConsumer(
-                ActivityTaskSupervisor::addToMultiWindowModeChangedList, this,
-                PooledLambda.__(ActivityRecord.class));
-        task.forAllActivities(c);
-        c.recycle();
+        task.forAllActivities(r -> {
+            if (r.attachedToProcess()) {
+                mMultiWindowModeChangedActivities.add(r);
+            }
+        });
 
         if (!mHandler.hasMessages(REPORT_MULTI_WINDOW_MODE_CHANGED_MSG)) {
             mHandler.sendEmptyMessage(REPORT_MULTI_WINDOW_MODE_CHANGED_MSG);
         }
     }
 
-    private void addToMultiWindowModeChangedList(ActivityRecord r) {
-        if (r.attachedToProcess()) {
-            mMultiWindowModeChangedActivities.add(r);
-        }
-    }
-
     void scheduleUpdatePictureInPictureModeIfNeeded(Task task, Task prevRootTask) {
         final Task rootTask = task.getRootTask();
         if ((prevRootTask == null || (prevRootTask != rootTask
@@ -2296,11 +2284,14 @@
     }
 
     void scheduleUpdatePictureInPictureModeIfNeeded(Task task, Rect targetRootTaskBounds) {
-        final PooledConsumer c = PooledLambda.obtainConsumer(
-                ActivityTaskSupervisor::addToPipModeChangedList, this,
-                PooledLambda.__(ActivityRecord.class));
-        task.forAllActivities(c);
-        c.recycle();
+        task.forAllActivities(r -> {
+            if (!r.attachedToProcess()) return;
+            mPipModeChangedActivities.add(r);
+            // If we are scheduling pip change, then remove this activity from multi-window
+            // change list as the processing of pip change will make sure multi-window changed
+            // message is processed in the right order relative to pip changed.
+            mMultiWindowModeChangedActivities.remove(r);
+        });
 
         mPipModeChangedTargetRootTaskBounds = targetRootTaskBounds;
 
@@ -2309,16 +2300,6 @@
         }
     }
 
-    private void addToPipModeChangedList(ActivityRecord r) {
-        if (!r.attachedToProcess()) return;
-
-        mPipModeChangedActivities.add(r);
-        // If we are scheduling pip change, then remove this activity from multi-window
-        // change list as the processing of pip change will make sure multi-window changed
-        // message is processed in the right order relative to pip changed.
-        mMultiWindowModeChangedActivities.remove(r);
-    }
-
     void wakeUp(String reason) {
         mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_APPLICATION,
                 "android.server.am:TURN_ON:" + reason);
diff --git a/services/core/java/com/android/server/wm/ContentRecorder.java b/services/core/java/com/android/server/wm/ContentRecorder.java
index acbf1a4..6e23ed9 100644
--- a/services/core/java/com/android/server/wm/ContentRecorder.java
+++ b/services/core/java/com/android/server/wm/ContentRecorder.java
@@ -391,8 +391,7 @@
      * </p>
      */
     private void handleStartRecordingFailed() {
-        final boolean shouldExitTaskRecording = mContentRecordingSession != null
-                && mContentRecordingSession.getContentToRecord() == RECORD_CONTENT_TASK;
+        final boolean shouldExitTaskRecording = isRecordingContentTask();
         clearContentRecordingSession();
         if (shouldExitTaskRecording) {
             // Clean up the cached session first to ensure recording doesn't re-start, since
@@ -478,9 +477,10 @@
         ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
                 "Recorded task is removed, so stop recording on display %d",
                 mDisplayContent.getDisplayId());
-        Task recordedTask = mRecordedWindowContainer.asTask();
-        if (recordedTask == null
-                || mContentRecordingSession.getContentToRecord() != RECORD_CONTENT_TASK) {
+
+        Task recordedTask = mRecordedWindowContainer != null
+                ? mRecordedWindowContainer.asTask() : null;
+        if (recordedTask == null || !isRecordingContentTask()) {
             return;
         }
         recordedTask.unregisterWindowContainerListener(this);
@@ -504,4 +504,9 @@
     @VisibleForTesting interface MediaProjectionManagerWrapper {
         void stopActiveProjection();
     }
+
+    private boolean isRecordingContentTask() {
+        return mContentRecordingSession != null
+                && mContentRecordingSession.getContentToRecord() == RECORD_CONTENT_TASK;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index c6fe017..669cd5d 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -241,7 +241,6 @@
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.ToBooleanFunction;
 import com.android.internal.util.function.TriConsumer;
-import com.android.internal.util.function.pooled.PooledConsumer;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.internal.util.function.pooled.PooledPredicate;
 import com.android.server.inputmethod.InputMethodManagerInternal;
@@ -3075,18 +3074,13 @@
      */
     boolean pointWithinAppWindow(int x, int y) {
         final int[] targetWindowType = {-1};
-        final PooledConsumer fn = PooledLambda.obtainConsumer((w, nonArg) -> {
-            if (targetWindowType[0] != -1) {
-                return;
-            }
-
+        forAllWindows(w -> {
             if (w.isOnScreen() && w.isVisible() && w.getFrame().contains(x, y)) {
                 targetWindowType[0] = w.mAttrs.type;
-                return;
+                return true;
             }
-        }, PooledLambda.__(WindowState.class), mTmpRect);
-        forAllWindows(fn, true /* traverseTopToBottom */);
-        fn.recycle();
+            return false;
+        }, true /* traverseTopToBottom */);
         return FIRST_APPLICATION_WINDOW <= targetWindowType[0]
                 && targetWindowType[0] <= LAST_APPLICATION_WINDOW;
     }
@@ -3112,11 +3106,7 @@
             mTmpRect.setEmpty();
             mTmpRect2.setEmpty();
 
-            final PooledConsumer c = PooledLambda.obtainConsumer(
-                    DisplayContent::processTaskForTouchExcludeRegion, this,
-                    PooledLambda.__(Task.class), focusedTask, delta);
-            forAllTasks(c);
-            c.recycle();
+            forAllTasks(t -> { processTaskForTouchExcludeRegion(t, focusedTask, delta); });
 
             // If we removed the focused task above, add it back and only leave its
             // outside touch area in the exclusion. TapDetector is not interested in
@@ -6187,17 +6177,10 @@
     /** Update and get all UIDs that are present on the display and have access to it. */
     IntArray getPresentUIDs() {
         mDisplayAccessUIDs.clear();
-        final PooledConsumer c = PooledLambda.obtainConsumer(DisplayContent::addActivityUid,
-                PooledLambda.__(ActivityRecord.class), mDisplayAccessUIDs);
-        mDisplayContent.forAllActivities(c);
-        c.recycle();
+        mDisplayContent.forAllActivities(r -> { mDisplayAccessUIDs.add(r.getUid()); });
         return mDisplayAccessUIDs;
     }
 
-    private static void addActivityUid(ActivityRecord r, IntArray uids) {
-        uids.add(r.getUid());
-    }
-
     @VisibleForTesting
     boolean shouldDestroyContentOnRemove() {
         return mDisplay.getRemoveMode() == REMOVE_MODE_DESTROY_CONTENT;
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index e780606..ac61cb9 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -2167,10 +2167,7 @@
      * If the decor insets changes, the display configuration may be affected. The caller should
      * call {@link DisplayContent#sendNewConfiguration()} if this method returns {@code true}.
      */
-    boolean updateDecorInsetsInfoIfNeeded(WindowState win) {
-        if (!win.providesNonDecorInsets()) {
-            return false;
-        }
+    boolean updateDecorInsetsInfo() {
         final DisplayFrames displayFrames = mDisplayContent.mDisplayFrames;
         final int rotation = displayFrames.mRotation;
         final int dw = displayFrames.mWidth;
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 9e01f10..bffab7a 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -62,8 +62,6 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.common.ProtoLog;
-import com.android.internal.util.function.pooled.PooledConsumer;
-import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.LocalServices;
 import com.android.server.inputmethod.InputMethodManagerInternal;
 import com.android.server.statusbar.StatusBarManagerInternal;
@@ -403,11 +401,11 @@
         final Task targetRootTask = mDisplayContent.getDefaultTaskDisplayArea()
                 .getRootTask(WINDOWING_MODE_UNDEFINED, targetActivityType);
         if (targetRootTask != null) {
-            final PooledConsumer c = PooledLambda.obtainConsumer((t, outList) ->
-	            { if (!outList.contains(t)) outList.add(t); }, PooledLambda.__(Task.class),
-                    visibleTasks);
-            targetRootTask.forAllLeafTasks(c, true /* traverseTopToBottom */);
-            c.recycle();
+            targetRootTask.forAllLeafTasks(t -> {
+                if (!visibleTasks.contains(t)) {
+                    visibleTasks.add(t);
+                }
+            }, true /* traverseTopToBottom */);
         }
 
         final int taskCount = visibleTasks.size();
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index ba834de..b2ec3f3 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -3102,9 +3102,8 @@
     }
 
     void finishVoiceTask(IVoiceInteractionSession session) {
-        forAllRootTasks(rootTask -> {
-            rootTask.finishVoiceTask(session);
-        });
+        final IBinder binder = session.asBinder();
+        forAllLeafTasks(t -> t.finishIfVoiceTask(binder), true /* traverseTopToBottom */);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java
index 120fec0..a753b55 100644
--- a/services/core/java/com/android/server/wm/RunningTasks.java
+++ b/services/core/java/com/android/server/wm/RunningTasks.java
@@ -23,18 +23,16 @@
 import android.os.UserHandle;
 import android.util.ArraySet;
 
-import com.android.internal.util.function.pooled.PooledConsumer;
-import com.android.internal.util.function.pooled.PooledLambda;
-
 import java.util.Comparator;
 import java.util.Iterator;
 import java.util.List;
 import java.util.TreeSet;
+import java.util.function.Consumer;
 
 /**
  * Class for resolving the set of running tasks in the system.
  */
-class RunningTasks {
+class RunningTasks implements Consumer<Task> {
 
     static final int FLAG_FILTER_ONLY_VISIBLE_RECENTS = 1;
     static final int FLAG_ALLOWED = 1 << 1;
@@ -61,7 +59,7 @@
     private boolean mKeepIntentExtra;
 
     void getTasks(int maxNum, List<RunningTaskInfo> list, int flags, RecentTasks recentTasks,
-            WindowContainer root, int callingUid, ArraySet<Integer> profileIds) {
+            WindowContainer<?> root, int callingUid, ArraySet<Integer> profileIds) {
         // Return early if there are no tasks to fetch
         if (maxNum <= 0) {
             return;
@@ -79,10 +77,7 @@
         mRecentTasks = recentTasks;
         mKeepIntentExtra = (flags & FLAG_KEEP_INTENT_EXTRA) == FLAG_KEEP_INTENT_EXTRA;
 
-        final PooledConsumer c = PooledLambda.obtainConsumer(RunningTasks::processTask, this,
-                PooledLambda.__(Task.class));
-        root.forAllLeafTasks(c, false);
-        c.recycle();
+        root.forAllLeafTasks(this, false /* traverseTopToBottom */);
 
         // Take the first {@param maxNum} tasks and create running task infos for them
         final Iterator<Task> iter = mTmpSortedSet.iterator();
@@ -97,7 +92,8 @@
         }
     }
 
-    private void processTask(Task task) {
+    @Override
+    public void accept(Task task) {
         if (task.getTopNonFinishingActivity() == null) {
             // Skip if there are no activities in the task
             return;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 1508870..63c6c35 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -196,7 +196,6 @@
 import com.android.internal.protolog.ProtoLogGroup;
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.XmlUtils;
-import com.android.internal.util.function.pooled.PooledConsumer;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.internal.util.function.pooled.PooledPredicate;
 import com.android.server.Watchdog;
@@ -1188,11 +1187,7 @@
         if (oldParent != null) {
             final Task oldParentTask = oldParent.asTask();
             if (oldParentTask != null) {
-                final PooledConsumer c = PooledLambda.obtainConsumer(
-                        Task::cleanUpActivityReferences, oldParentTask,
-                        PooledLambda.__(ActivityRecord.class));
-                forAllActivities(c);
-                c.recycle();
+                forAllActivities(oldParentTask::cleanUpActivityReferences);
             }
 
             if (oldParent.inPinnedWindowingMode()
@@ -2371,10 +2366,7 @@
 
     int getDescendantTaskCount() {
         final int[] currentCount = {0};
-        final PooledConsumer c = PooledLambda.obtainConsumer((t, count) -> { count[0]++; },
-                PooledLambda.__(Task.class), currentCount);
-        forAllLeafTasks(c, false /* traverseTopToBottom */);
-        c.recycle();
+        forAllLeafTasks(t -> currentCount[0]++, false /* traverseTopToBottom */);
         return currentCount[0];
     }
 
@@ -2783,10 +2775,7 @@
                 && displayContent.mDividerControllerLocked.isResizing();
         if (inFreeformWindowingMode()) {
             boolean[] foundTop = { false };
-            final PooledConsumer c = PooledLambda.obtainConsumer(Task::getMaxVisibleBounds,
-                    PooledLambda.__(ActivityRecord.class), out, foundTop);
-            forAllActivities(c);
-            c.recycle();
+            forAllActivities(a -> { getMaxVisibleBounds(a, out, foundTop); });
             if (foundTop[0]) {
                 return;
             }
@@ -4349,14 +4338,6 @@
         }
     }
 
-
-    void setActivityWindowingMode(int windowingMode) {
-        PooledConsumer c = PooledLambda.obtainConsumer(ActivityRecord::setWindowingMode,
-                PooledLambda.__(ActivityRecord.class), windowingMode);
-        forAllActivities(c);
-        c.recycle();
-    }
-
     /**
      * Sets/unsets the forced-hidden state flag for this task depending on {@param set}.
      * @return Whether the force hidden state changed
@@ -5209,26 +5190,19 @@
         return finishedTask;
     }
 
-    void finishVoiceTask(IVoiceInteractionSession session) {
-        final PooledConsumer c = PooledLambda.obtainConsumer(Task::finishIfVoiceTask,
-                PooledLambda.__(Task.class), session.asBinder());
-        forAllLeafTasks(c, true /* traverseTopToBottom */);
-        c.recycle();
-    }
-
-    private static void finishIfVoiceTask(Task tr, IBinder binder) {
-        if (tr.voiceSession != null && tr.voiceSession.asBinder() == binder) {
-            tr.forAllActivities((r) -> {
+    void finishIfVoiceTask(IBinder binder) {
+        if (voiceSession != null && voiceSession.asBinder() == binder) {
+            forAllActivities((r) -> {
                 if (r.finishing) return;
                 r.finishIfPossible("finish-voice", false /* oomAdj */);
-                tr.mAtmService.updateOomAdj();
+                mAtmService.updateOomAdj();
             });
         } else {
             // Check if any of the activities are using voice
             final PooledPredicate f = PooledLambda.obtainPredicate(
                     Task::finishIfVoiceActivity, PooledLambda.__(ActivityRecord.class),
                     binder);
-            tr.forAllActivities(f);
+            forAllActivities(f);
             f.recycle();
         }
     }
@@ -5447,10 +5421,7 @@
 
         if (timeTracker != null) {
             // The caller wants a time tracker associated with this task.
-            final PooledConsumer c = PooledLambda.obtainConsumer(ActivityRecord::setAppTimeTracker,
-                    PooledLambda.__(ActivityRecord.class), timeTracker);
-            tr.forAllActivities(c);
-            c.recycle();
+            tr.forAllActivities(a -> { a.appTimeTracker = timeTracker; });
         }
 
         try {
@@ -5621,11 +5592,11 @@
         try {
             // TODO: Why not just set this on the root task directly vs. on each tasks?
             // Update override configurations of all tasks in the root task.
-            final PooledConsumer c = PooledLambda.obtainConsumer(
-                    Task::processTaskResizeBounds, PooledLambda.__(Task.class),
-                    displayedBounds);
-            forAllTasks(c, true /* traverseTopToBottom */);
-            c.recycle();
+            forAllTasks(task -> {
+                if (task.isResizeable()) {
+                    task.setBounds(displayedBounds);
+                }
+            }, true /* traverseTopToBottom */);
 
             if (!deferResume) {
                 ensureVisibleActivitiesConfiguration(topRunningActivity(), preserveWindows);
@@ -5636,12 +5607,6 @@
         }
     }
 
-    private static void processTaskResizeBounds(Task task, Rect displayedBounds) {
-        if (!task.isResizeable()) return;
-
-        task.setBounds(displayedBounds);
-    }
-
     boolean willActivityBeVisible(IBinder token) {
         final ActivityRecord r = ActivityRecord.forTokenLocked(token);
         if (r == null) {
@@ -5727,22 +5692,15 @@
 
         // All activities that came from the package must be
         // restarted as if there was a config change.
-        PooledConsumer c = PooledLambda.obtainConsumer(Task::restartPackage,
-                PooledLambda.__(ActivityRecord.class), starting, packageName);
-        forAllActivities(c);
-        c.recycle();
-
-        return starting;
-    }
-
-    private static void restartPackage(
-            ActivityRecord r, ActivityRecord starting, String packageName) {
-        if (r.info.packageName.equals(packageName)) {
+        forAllActivities(r -> {
+            if (!r.info.packageName.equals(packageName)) return;
             r.forceNewConfig = true;
             if (starting != null && r == starting && r.mVisibleRequested) {
                 r.startFreezingScreenLocked(CONFIG_SCREEN_LAYOUT);
             }
-        }
+        });
+
+        return starting;
     }
 
     Task reuseOrCreateTask(ActivityInfo info, Intent intent, boolean toTop) {
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 2cfc563..4f1a561 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -1719,7 +1719,7 @@
                 ProtoLog.v(WM_DEBUG_STATES, "Executing finish of activity: %s", prev);
                 prev = prev.completeFinishing(false /* updateVisibility */,
                         "completePausedLocked");
-            } else if (prev.hasProcess()) {
+            } else if (prev.attachedToProcess()) {
                 ProtoLog.v(WM_DEBUG_STATES, "Enqueue pending stop if needed: %s "
                                 + "wasStopping=%b visibleRequested=%b",  prev,  wasStopping,
                         prev.mVisibleRequested);
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 69d86b6..9763df6 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -477,7 +477,10 @@
             }
             mLocalInsetsSourceProviders.remove(insetsTypes[i]);
         }
-        mDisplayContent.getInsetsStateController().updateAboveInsetsState(true);
+        // Update insets if this window is attached.
+        if (mDisplayContent != null) {
+            mDisplayContent.getInsetsStateController().updateAboveInsetsState(true);
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index e23e206..29ab562 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -309,8 +309,6 @@
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.LatencyTracker;
-import com.android.internal.util.function.pooled.PooledConsumer;
-import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.internal.view.WindowManagerPolicyThread;
 import com.android.server.AnimationThread;
 import com.android.server.DisplayThread;
@@ -475,10 +473,7 @@
         public void onVrStateChanged(boolean enabled) {
             synchronized (mGlobalLock) {
                 mVrModeEnabled = enabled;
-                final PooledConsumer c = PooledLambda.obtainConsumer(
-                        DisplayPolicy::onVrStateChangedLw, PooledLambda.__(), enabled);
-                mRoot.forAllDisplayPolicies(c);
-                c.recycle();
+                mRoot.forAllDisplayPolicies(p -> p.onVrStateChangedLw(enabled));
             }
         }
     };
@@ -900,11 +895,8 @@
             }
             mPointerLocationEnabled = enablePointerLocation;
             synchronized (mGlobalLock) {
-                final PooledConsumer c = PooledLambda.obtainConsumer(
-                        DisplayPolicy::setPointerLocationEnabled, PooledLambda.__(),
-                        mPointerLocationEnabled);
-                mRoot.forAllDisplayPolicies(c);
-                c.recycle();
+                mRoot.forAllDisplayPolicies(
+                        p -> p.setPointerLocationEnabled(mPointerLocationEnabled));
             }
         }
 
@@ -1834,8 +1826,12 @@
             ProtoLog.v(WM_DEBUG_ADD_REMOVE, "addWindow: New client %s"
                     + ": window=%s Callers=%s", client.asBinder(), win, Debug.getCallers(5));
 
-            if ((win.isVisibleRequestedOrAdding() && displayContent.updateOrientation())
-                    || displayPolicy.updateDecorInsetsInfoIfNeeded(win)) {
+            boolean needToSendNewConfiguration =
+                    win.isVisibleRequestedOrAdding() && displayContent.updateOrientation();
+            if (win.providesNonDecorInsets()) {
+                needToSendNewConfiguration |= displayPolicy.updateDecorInsetsInfo();
+            }
+            if (needToSendNewConfiguration) {
                 displayContent.sendNewConfiguration();
             }
 
@@ -2304,8 +2300,8 @@
                         & WindowManager.LayoutParams.SYSTEM_UI_VISIBILITY_CHANGED) != 0) {
                     win.mLayoutNeeded = true;
                 }
-                if (layoutChanged) {
-                    configChanged = displayPolicy.updateDecorInsetsInfoIfNeeded(win);
+                if (layoutChanged && win.providesNonDecorInsets()) {
+                    configChanged = displayPolicy.updateDecorInsetsInfo();
                 }
                 if (win.mActivityRecord != null && ((flagChanges & FLAG_SHOW_WHEN_LOCKED) != 0
                         || (flagChanges & FLAG_DISMISS_KEYGUARD) != 0)) {
@@ -3135,10 +3131,7 @@
 
     @Override
     public void onPowerKeyDown(boolean isScreenOn) {
-        final PooledConsumer c = PooledLambda.obtainConsumer(
-                DisplayPolicy::onPowerKeyDown, PooledLambda.__(), isScreenOn);
-        mRoot.forAllDisplayPolicies(c);
-        c.recycle();
+        mRoot.forAllDisplayPolicies(p -> p.onPowerKeyDown(isScreenOn));
     }
 
     @Override
@@ -8387,10 +8380,7 @@
     void onLockTaskStateChanged(int lockTaskState) {
         // TODO: pass in displayId to determine which display the lock task state changed
         synchronized (mGlobalLock) {
-            final PooledConsumer c = PooledLambda.obtainConsumer(
-                    DisplayPolicy::onLockTaskStateChangedLw, PooledLambda.__(), lockTaskState);
-            mRoot.forAllDisplayPolicies(c);
-            c.recycle();
+            mRoot.forAllDisplayPolicies(p -> p.onLockTaskStateChangedLw(lockTaskState));
         }
     }
 
@@ -9282,4 +9272,46 @@
                         "Unexpected letterbox background type: " + letterboxBackgroundType);
         }
     }
+
+    @Override
+    public void captureDisplay(int displayId, @Nullable ScreenCapture.CaptureArgs captureArgs,
+            ScreenCapture.ScreenCaptureListener listener) {
+        Slog.d(TAG, "captureDisplay");
+        if (!checkCallingPermission(READ_FRAME_BUFFER, "captureDisplay()")) {
+            throw new SecurityException("Requires READ_FRAME_BUFFER permission");
+        }
+
+        ScreenCapture.captureLayers(getCaptureArgs(displayId, captureArgs), listener);
+    }
+
+    @VisibleForTesting
+    ScreenCapture.LayerCaptureArgs getCaptureArgs(int displayId,
+            @Nullable ScreenCapture.CaptureArgs captureArgs) {
+        final SurfaceControl displaySurfaceControl;
+        synchronized (mGlobalLock) {
+            DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+            if (displayContent == null) {
+                throw new IllegalArgumentException("Trying to screenshot and invalid display: "
+                        + displayId);
+            }
+
+            displaySurfaceControl = displayContent.getSurfaceControl();
+
+            if (captureArgs == null) {
+                captureArgs = new ScreenCapture.CaptureArgs.Builder<>()
+                        .build();
+            }
+
+            if (captureArgs.mSourceCrop.isEmpty()) {
+                displayContent.getBounds(mTmpRect);
+                mTmpRect.offsetTo(0, 0);
+            } else {
+                mTmpRect.set(captureArgs.mSourceCrop);
+            }
+        }
+
+        return new ScreenCapture.LayerCaptureArgs.Builder(displaySurfaceControl, captureArgs)
+                        .setSourceCrop(mTmpRect)
+                        .build();
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 2e1477d..e2f833c 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -91,8 +91,6 @@
 import com.android.internal.protolog.ProtoLogGroup;
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.function.pooled.PooledConsumer;
-import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.LocalServices;
 import com.android.server.pm.LauncherAppsService.LauncherAppsServiceInternal;
 
@@ -482,7 +480,7 @@
             }
             final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps();
             final int hopSize = hops.size();
-            ArraySet<WindowContainer> haveConfigChanges = new ArraySet<>();
+            final ArraySet<WindowContainer<?>> haveConfigChanges = new ArraySet<>();
             Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> entries =
                     t.getChanges().entrySet().iterator();
             while (entries.hasNext()) {
@@ -592,16 +590,10 @@
                 mService.mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
                 mService.mRootWindowContainer.resumeFocusedTasksTopActivities();
             } else if ((effects & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) {
-                final PooledConsumer f = PooledLambda.obtainConsumer(
-                        ActivityRecord::ensureActivityConfiguration,
-                        PooledLambda.__(ActivityRecord.class), 0,
-                        true /* preserveWindow */);
-                try {
-                    for (int i = haveConfigChanges.size() - 1; i >= 0; --i) {
-                        haveConfigChanges.valueAt(i).forAllActivities(f);
-                    }
-                } finally {
-                    f.recycle();
+                for (int i = haveConfigChanges.size() - 1; i >= 0; --i) {
+                    haveConfigChanges.valueAt(i).forAllActivities(r -> {
+                        r.ensureActivityConfiguration(0, PRESERVE_WINDOWS);
+                    });
                 }
             }
 
@@ -716,7 +708,7 @@
 
         final int childWindowingMode = c.getActivityWindowingMode();
         if (childWindowingMode > -1) {
-            tr.setActivityWindowingMode(childWindowingMode);
+            tr.forAllActivities(a -> { a.setWindowingMode(childWindowingMode); });
         }
 
         if (t != null) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 7434ea0..68fabc5 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2626,11 +2626,19 @@
                 }
             }
 
+            // Check if window provides non decor insets before clearing its provided insets.
+            final boolean windowProvidesNonDecorInsets = providesNonDecorInsets();
+
             removeImmediately();
             // Removing a visible window may affect the display orientation so just update it if
             // needed. Also recompute configuration if it provides screen decor insets.
-            if ((wasVisible && displayContent.updateOrientation())
-                    || displayContent.getDisplayPolicy().updateDecorInsetsInfoIfNeeded(this)) {
+            boolean needToSendNewConfiguration = wasVisible && displayContent.updateOrientation();
+            if (windowProvidesNonDecorInsets) {
+                needToSendNewConfiguration |=
+                        displayContent.getDisplayPolicy().updateDecorInsetsInfo();
+            }
+
+            if (needToSendNewConfiguration) {
                 displayContent.sendNewConfiguration();
             }
             mWmService.updateFocusedWindowLocked(isFocused()
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 78b4ce2..3f380e7 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -168,8 +168,9 @@
     jmethodID constructor;
     jfieldID lightTypeInput;
     jfieldID lightTypePlayerId;
+    jfieldID lightTypeKeyboardBacklight;
     jfieldID lightCapabilityBrightness;
-    jfieldID lightCapabilityRgb;
+    jfieldID lightCapabilityColorRgb;
 } gLightClassInfo;
 
 static struct {
@@ -2011,25 +2012,28 @@
 
         jint jTypeId =
                 env->GetStaticIntField(gLightClassInfo.clazz, gLightClassInfo.lightTypeInput);
-        jint jCapability = 0;
-
-        if (lightInfo.type == InputDeviceLightType::MONO) {
-            jCapability = env->GetStaticIntField(gLightClassInfo.clazz,
-                                                 gLightClassInfo.lightCapabilityBrightness);
-        } else if (lightInfo.type == InputDeviceLightType::RGB ||
-                   lightInfo.type == InputDeviceLightType::MULTI_COLOR) {
-            jCapability =
-                env->GetStaticIntField(gLightClassInfo.clazz,
-                                                 gLightClassInfo.lightCapabilityBrightness) |
-                env->GetStaticIntField(gLightClassInfo.clazz,
-                                                 gLightClassInfo.lightCapabilityRgb);
+        if (lightInfo.type == InputDeviceLightType::INPUT) {
+            jTypeId = env->GetStaticIntField(gLightClassInfo.clazz, gLightClassInfo.lightTypeInput);
         } else if (lightInfo.type == InputDeviceLightType::PLAYER_ID) {
             jTypeId = env->GetStaticIntField(gLightClassInfo.clazz,
                                                  gLightClassInfo.lightTypePlayerId);
+        } else if (lightInfo.type == InputDeviceLightType::KEYBOARD_BACKLIGHT) {
+            jTypeId = env->GetStaticIntField(gLightClassInfo.clazz,
+                                             gLightClassInfo.lightTypeKeyboardBacklight);
         } else {
             ALOGW("Unknown light type %d", lightInfo.type);
             continue;
         }
+
+        jint jCapability = 0;
+        if (lightInfo.capabilityFlags.test(InputDeviceLightCapability::BRIGHTNESS)) {
+            jCapability |= env->GetStaticIntField(gLightClassInfo.clazz,
+                                                  gLightClassInfo.lightCapabilityBrightness);
+        }
+        if (lightInfo.capabilityFlags.test(InputDeviceLightCapability::RGB)) {
+            jCapability |= env->GetStaticIntField(gLightClassInfo.clazz,
+                                                  gLightClassInfo.lightCapabilityColorRgb);
+        }
         ScopedLocalRef<jobject> lightObj(env,
                                          env->NewObject(gLightClassInfo.clazz,
                                                         gLightClassInfo.constructor,
@@ -2596,10 +2600,12 @@
             env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_TYPE_INPUT", "I");
     gLightClassInfo.lightTypePlayerId =
             env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_TYPE_PLAYER_ID", "I");
+    gLightClassInfo.lightTypeKeyboardBacklight =
+            env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_TYPE_KEYBOARD_BACKLIGHT", "I");
     gLightClassInfo.lightCapabilityBrightness =
             env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_CAPABILITY_BRIGHTNESS", "I");
-    gLightClassInfo.lightCapabilityRgb =
-            env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_CAPABILITY_RGB", "I");
+    gLightClassInfo.lightCapabilityColorRgb =
+            env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_CAPABILITY_COLOR_RGB", "I");
 
     // ArrayList
     FIND_CLASS(gArrayListClassInfo.clazz, "java/util/ArrayList");
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 6196c49..9c9b363 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -1287,8 +1287,8 @@
         bp.set_allocated_dest_path(&target);
         bp.set_allocated_source_subdir(&source);
         const auto metadata = bp.SerializeAsString();
-        bp.release_dest_path();
-        bp.release_source_subdir();
+        static_cast<void>(bp.release_dest_path());
+        static_cast<void>(bp.release_source_subdir());
         mdFileName = makeBindMdName();
         metadataFullPath = path::join(ifs.root, constants().mount, mdFileName);
         auto node = mIncFs->makeFile(ifs.control, metadataFullPath, 0444, idFromMetadata(metadata),
diff --git a/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerServiceImpl.java b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerServiceImpl.java
index 525a931..c8d153a 100644
--- a/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerServiceImpl.java
+++ b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerServiceImpl.java
@@ -23,7 +23,6 @@
 import android.content.ComponentName;
 import android.content.pm.PackageManager;
 import android.content.pm.ServiceInfo;
-import android.hardware.input.InputManagerInternal;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -35,6 +34,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.server.LocalServices;
 import com.android.server.infra.AbstractPerUserSystemService;
+import com.android.server.input.InputManagerInternal;
 
 final class SelectionToolbarManagerServiceImpl extends
         AbstractPerUserSystemService<SelectionToolbarManagerServiceImpl,
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
index cf5d113..89accf8 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -17,6 +17,7 @@
 package com.android.server.am;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -42,6 +43,7 @@
 import android.app.AppOpsManager;
 import android.app.BroadcastOptions;
 import android.app.IApplicationThread;
+import android.app.RemoteServiceException.CannotDeliverBroadcastException;
 import android.app.usage.UsageEvents.Event;
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.ComponentName;
@@ -56,6 +58,7 @@
 import android.content.pm.ResolveInfo;
 import android.os.Binder;
 import android.os.Bundle;
+import android.os.DeadObjectException;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
@@ -136,6 +139,12 @@
     private BroadcastQueue mQueue;
 
     /**
+     * When enabled {@link ActivityManagerService#startProcessLocked} will fail
+     * by returning {@code null}; otherwise it will spawn a new mock process.
+     */
+    private boolean mFailStartProcess;
+
+    /**
      * Map from PID to registered registered runtime receivers.
      */
     private SparseArray<ReceiverList> mRegisteredReceivers = new SparseArray<>();
@@ -185,10 +194,13 @@
         doAnswer((invocation) -> {
             Log.v(TAG, "Intercepting startProcessLocked() for "
                     + Arrays.toString(invocation.getArguments()));
+            if (mFailStartProcess) {
+                return null;
+            }
             final String processName = invocation.getArgument(0);
             final ApplicationInfo ai = invocation.getArgument(1);
-            final ProcessRecord res = makeActiveProcessRecord(ai, processName, false,
-                    false, UnaryOperator.identity());
+            final ProcessRecord res = makeActiveProcessRecord(ai, processName,
+                    ProcessBehavior.NORMAL, UnaryOperator.identity());
             mHandlerThread.getThreadHandler().post(() -> {
                 synchronized (mAms) {
                     mQueue.onApplicationAttachedLocked(res);
@@ -197,6 +209,16 @@
             return res;
         }).when(mAms).startProcessLocked(any(), any(), anyBoolean(), anyInt(),
                 any(), anyInt(), anyBoolean(), anyBoolean());
+        doAnswer((invocation) -> {
+            final String processName = invocation.getArgument(0);
+            final int uid = invocation.getArgument(1);
+            for (ProcessRecord r : mActiveProcesses) {
+                if (Objects.equals(r.processName, processName) && r.uid == uid) {
+                    return r;
+                }
+            }
+            return null;
+        }).when(mAms).getProcessRecordLocked(any(), anyInt());
         doNothing().when(mAms).appNotResponding(any(), any());
 
         final BroadcastConstants constants = new BroadcastConstants(
@@ -278,29 +300,51 @@
         }
     }
 
+    private enum ProcessBehavior {
+        /** Process broadcasts normally */
+        NORMAL,
+        /** Wedge and never confirm broadcast receipt */
+        WEDGE,
+        /** Process broadcast by requesting abort */
+        ABORT,
+        /** Appear to behave completely dead */
+        DEAD,
+    }
+
     private ProcessRecord makeActiveProcessRecord(String packageName) throws Exception {
         final ApplicationInfo ai = makeApplicationInfo(packageName);
-        return makeActiveProcessRecord(ai, ai.processName, false, false,
+        return makeActiveProcessRecord(ai, ai.processName, ProcessBehavior.NORMAL,
                 UnaryOperator.identity());
     }
 
-    private ProcessRecord makeWedgedActiveProcessRecord(String packageName) throws Exception {
+    private ProcessRecord makeActiveProcessRecord(String packageName,
+            ProcessBehavior behavior) throws Exception {
         final ApplicationInfo ai = makeApplicationInfo(packageName);
-        return makeActiveProcessRecord(ai, ai.processName, true, false,
+        return makeActiveProcessRecord(ai, ai.processName, behavior,
                 UnaryOperator.identity());
     }
 
     private ProcessRecord makeActiveProcessRecord(ApplicationInfo ai, String processName,
-            boolean wedged, boolean abort, UnaryOperator<Bundle> extrasOperator) throws Exception {
+            ProcessBehavior behavior, UnaryOperator<Bundle> extrasOperator) throws Exception {
+        final boolean wedge = (behavior == ProcessBehavior.WEDGE);
+        final boolean abort = (behavior == ProcessBehavior.ABORT);
+        final boolean dead = (behavior == ProcessBehavior.DEAD);
+
         final ProcessRecord r = spy(new ProcessRecord(mAms, ai, processName, ai.uid));
         r.setPid(mNextPid.getAndIncrement());
         mActiveProcesses.add(r);
 
-        final IApplicationThread thread = mock(IApplicationThread.class);
+        final IApplicationThread thread;
+        if (dead) {
+            thread = mock(IApplicationThread.class, (invocation) -> {
+                throw new DeadObjectException();
+            });
+        } else {
+            thread = mock(IApplicationThread.class);
+        }
         final IBinder threadBinder = new Binder();
         doReturn(threadBinder).when(thread).asBinder();
         r.makeActive(thread, mAms.mProcessStats);
-        doReturn(r).when(mAms).getProcessRecordLocked(eq(r.info.processName), eq(r.info.uid));
 
         final IIntentReceiver receiver = mock(IIntentReceiver.class);
         final IBinder receiverBinder = new Binder();
@@ -310,17 +354,28 @@
         mRegisteredReceivers.put(r.getPid(), receiverList);
 
         doAnswer((invocation) -> {
+            Log.v(TAG, "Intercepting killLocked() for "
+                    + Arrays.toString(invocation.getArguments()));
+            mActiveProcesses.remove(r);
+            mRegisteredReceivers.remove(r.getPid());
+            return invocation.callRealMethod();
+        }).when(r).killLocked(any(), any(), anyInt(), anyInt(), anyBoolean());
+
+        // If we're entirely dead, rely on default behaviors above
+        if (dead) return r;
+
+        doAnswer((invocation) -> {
             Log.v(TAG, "Intercepting scheduleReceiver() for "
                     + Arrays.toString(invocation.getArguments()));
             final Bundle extras = invocation.getArgument(5);
-            if (!wedged) {
+            if (!wedge) {
                 assertTrue(r.mReceivers.numberOfCurReceivers() > 0);
                 assertTrue(mQueue.getPreferredSchedulingGroupLocked(r)
                         != ProcessList.SCHED_GROUP_UNDEFINED);
                 mHandlerThread.getThreadHandler().post(() -> {
                     synchronized (mAms) {
-                        mQueue.finishReceiverLocked(r, Activity.RESULT_OK,
-                                null, extrasOperator.apply(extras), abort, false);
+                        mQueue.finishReceiverLocked(r, Activity.RESULT_OK, null,
+                                extrasOperator.apply(extras), abort, false);
                     }
                 });
             }
@@ -333,7 +388,7 @@
                     + Arrays.toString(invocation.getArguments()));
             final Bundle extras = invocation.getArgument(4);
             final boolean ordered = invocation.getArgument(5);
-            if (!wedged && ordered) {
+            if (!wedge && ordered) {
                 assertTrue(r.mReceivers.numberOfCurReceivers() > 0);
                 assertTrue(mQueue.getPreferredSchedulingGroupLocked(r)
                         != ProcessList.SCHED_GROUP_UNDEFINED);
@@ -449,6 +504,13 @@
                 any(), eq(false), eq(UserHandle.USER_SYSTEM), anyInt());
     }
 
+    private void verifyScheduleReceiver(VerificationMode mode, ProcessRecord app, Intent intent)
+            throws Exception {
+        verify(app.getThread(), mode).scheduleReceiver(
+                argThat(filterEqualsIgnoringComponent(intent)), any(), any(), anyInt(), any(),
+                any(), eq(false), eq(UserHandle.USER_SYSTEM), anyInt());
+    }
+
     private void verifyScheduleReceiver(VerificationMode mode, ProcessRecord app, Intent intent,
             ComponentName component) throws Exception {
         final Intent targetedIntent = new Intent(intent);
@@ -689,7 +751,8 @@
     @Test
     public void testWedged() throws Exception {
         final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
-        final ProcessRecord receiverApp = makeWedgedActiveProcessRecord(PACKAGE_GREEN);
+        final ProcessRecord receiverApp = makeActiveProcessRecord(PACKAGE_GREEN,
+                ProcessBehavior.WEDGE);
 
         final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
         enqueueBroadcast(makeBroadcastRecord(airplane, callerApp,
@@ -700,6 +763,106 @@
     }
 
     /**
+     * Verify that we handle registered receivers in a process that always
+     * responds with {@link DeadObjectException}, recovering to restart the
+     * process and deliver their next broadcast.
+     */
+    @Test
+    public void testDead_Registered() throws Exception {
+        final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
+        final ProcessRecord receiverApp = makeActiveProcessRecord(PACKAGE_GREEN,
+                ProcessBehavior.DEAD);
+
+        final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+        enqueueBroadcast(makeBroadcastRecord(airplane, callerApp,
+                List.of(makeRegisteredReceiver(receiverApp))));
+        final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
+        enqueueBroadcast(makeBroadcastRecord(timezone, callerApp,
+                List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN))));
+        waitForIdle();
+
+        // First broadcast should have already been dead
+        verifyScheduleRegisteredReceiver(receiverApp, airplane);
+        verify(receiverApp).scheduleCrashLocked(any(),
+                eq(CannotDeliverBroadcastException.TYPE_ID), any());
+
+        // Second broadcast in new process should work fine
+        final ProcessRecord restartedReceiverApp = mAms.getProcessRecordLocked(PACKAGE_GREEN,
+                getUidForPackage(PACKAGE_GREEN));
+        assertNotEquals(receiverApp, restartedReceiverApp);
+        verifyScheduleReceiver(restartedReceiverApp, timezone);
+    }
+
+    /**
+     * Verify that we handle manifest receivers in a process that always
+     * responds with {@link DeadObjectException}, recovering to restart the
+     * process and deliver their next broadcast.
+     */
+    @Test
+    public void testDead_Manifest() throws Exception {
+        final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
+        final ProcessRecord receiverApp = makeActiveProcessRecord(PACKAGE_GREEN,
+                ProcessBehavior.DEAD);
+
+        final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+        enqueueBroadcast(makeBroadcastRecord(airplane, callerApp,
+                List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN))));
+        final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
+        enqueueBroadcast(makeBroadcastRecord(timezone, callerApp,
+                List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN))));
+        waitForIdle();
+
+        // First broadcast should have already been dead
+        verifyScheduleReceiver(receiverApp, airplane);
+        verify(receiverApp).scheduleCrashLocked(any(),
+                eq(CannotDeliverBroadcastException.TYPE_ID), any());
+
+        // Second broadcast in new process should work fine
+        final ProcessRecord restartedReceiverApp = mAms.getProcessRecordLocked(PACKAGE_GREEN,
+                getUidForPackage(PACKAGE_GREEN));
+        assertNotEquals(receiverApp, restartedReceiverApp);
+        verifyScheduleReceiver(restartedReceiverApp, timezone);
+    }
+
+    /**
+     * Verify that we handle the system failing to start a process.
+     */
+    @Test
+    public void testFailStartProcess() throws Exception {
+        final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
+
+        // Send broadcast while process starts are failing
+        mFailStartProcess = true;
+        final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+        enqueueBroadcast(makeBroadcastRecord(airplane, callerApp,
+                List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN),
+                        makeManifestReceiver(PACKAGE_YELLOW, CLASS_YELLOW))));
+
+        // Confirm that queue goes idle, with no processes
+        waitForIdle();
+        assertEquals(1, mActiveProcesses.size());
+
+        // Send more broadcasts with working process starts
+        mFailStartProcess = false;
+        final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
+        enqueueBroadcast(makeBroadcastRecord(timezone, callerApp,
+                List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN),
+                        makeManifestReceiver(PACKAGE_YELLOW, CLASS_YELLOW))));
+
+        // Confirm that we only saw second broadcast
+        waitForIdle();
+        assertEquals(3, mActiveProcesses.size());
+        final ProcessRecord receiverGreenApp = mAms.getProcessRecordLocked(PACKAGE_GREEN,
+                getUidForPackage(PACKAGE_GREEN));
+        final ProcessRecord receiverYellowApp = mAms.getProcessRecordLocked(PACKAGE_YELLOW,
+                getUidForPackage(PACKAGE_YELLOW));
+        verifyScheduleReceiver(never(), receiverGreenApp, airplane);
+        verifyScheduleReceiver(never(), receiverYellowApp, airplane);
+        verifyScheduleReceiver(times(1), receiverGreenApp, timezone);
+        verifyScheduleReceiver(times(1), receiverYellowApp, timezone);
+    }
+
+    /**
      * Verify that we cleanup a disabled component, skipping a pending dispatch
      * of broadcast to that component.
      */
@@ -742,6 +905,45 @@
     }
 
     /**
+     * Verify that killing a running process skips registered receivers.
+     */
+    @Test
+    public void testKill() throws Exception {
+        final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
+        final ProcessRecord oldApp = makeActiveProcessRecord(PACKAGE_GREEN);
+
+        final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+        try (SyncBarrier b = new SyncBarrier()) {
+            enqueueBroadcast(makeBroadcastRecord(airplane, callerApp, new ArrayList<>(
+                    List.of(makeRegisteredReceiver(oldApp),
+                            makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN)))));
+
+            synchronized (mAms) {
+                oldApp.killLocked(TAG, 42, false);
+                mQueue.onApplicationCleanupLocked(oldApp);
+            }
+        }
+        waitForIdle();
+
+        // Confirm that we cold-started after the kill
+        final ProcessRecord newApp = mAms.getProcessRecordLocked(PACKAGE_GREEN,
+                getUidForPackage(PACKAGE_GREEN));
+        assertNotEquals(oldApp, newApp);
+
+        // Confirm that we saw no registered receiver traffic
+        final IApplicationThread oldThread = oldApp.getThread();
+        verify(oldThread, never()).scheduleRegisteredReceiver(any(),
+                any(), anyInt(), any(), any(), anyBoolean(), anyBoolean(), anyInt(), anyInt());
+        final IApplicationThread newThread = newApp.getThread();
+        verify(newThread, never()).scheduleRegisteredReceiver(any(),
+                any(), anyInt(), any(), any(), anyBoolean(), anyBoolean(), anyInt(), anyInt());
+
+        // Confirm that we saw final manifest broadcast
+        verifyScheduleReceiver(times(1), newApp, airplane,
+                new ComponentName(PACKAGE_GREEN, CLASS_GREEN));
+    }
+
+    /**
      * Verify that we skip broadcasts to an app being backed up.
      */
     @Test
@@ -770,13 +972,13 @@
         // Purposefully warm-start the middle apps to make sure we dispatch to
         // both cold and warm apps in expected order
         makeActiveProcessRecord(makeApplicationInfo(PACKAGE_BLUE), PACKAGE_BLUE,
-                false, false, (extras) -> {
+                ProcessBehavior.NORMAL, (extras) -> {
                     extras = clone(extras);
                     extras.putBoolean(PACKAGE_BLUE, true);
                     return extras;
                 });
         makeActiveProcessRecord(makeApplicationInfo(PACKAGE_YELLOW), PACKAGE_YELLOW,
-                false, false, (extras) -> {
+                ProcessBehavior.NORMAL, (extras) -> {
                     extras = clone(extras);
                     extras.putBoolean(PACKAGE_YELLOW, true);
                     return extras;
@@ -858,7 +1060,7 @@
 
         // Create a process that aborts any ordered broadcasts
         makeActiveProcessRecord(makeApplicationInfo(PACKAGE_GREEN), PACKAGE_GREEN,
-                false, true, (extras) -> {
+                ProcessBehavior.ABORT, (extras) -> {
                     extras = clone(extras);
                     extras.putBoolean(PACKAGE_GREEN, true);
                     return extras;
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
index 5d9d765..6b8c26d 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
@@ -25,7 +25,6 @@
 
 import android.hardware.display.DisplayManagerInternal;
 import android.hardware.input.IInputManager;
-import android.hardware.input.InputManagerInternal;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
@@ -39,6 +38,7 @@
 import androidx.test.InstrumentationRegistry;
 
 import com.android.server.LocalServices;
+import com.android.server.input.InputManagerInternal;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index ef203d0..57ded99 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -56,7 +56,6 @@
 import android.graphics.Point;
 import android.hardware.display.DisplayManagerInternal;
 import android.hardware.input.IInputManager;
-import android.hardware.input.InputManagerInternal;
 import android.hardware.input.VirtualKeyEvent;
 import android.hardware.input.VirtualMouseButtonEvent;
 import android.hardware.input.VirtualMouseRelativeEvent;
@@ -84,6 +83,7 @@
 
 import com.android.internal.app.BlockedAppStreamingActivity;
 import com.android.server.LocalServices;
+import com.android.server.input.InputManagerInternal;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
index 8280fc6..fc2a4cf 100644
--- a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -38,7 +38,6 @@
 import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
 import android.os.Handler;
 import android.os.test.TestLooper;
-import android.platform.test.annotations.Presubmit;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
@@ -56,7 +55,6 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
-@Presubmit
 @RunWith(AndroidJUnit4.class)
 public class AutomaticBrightnessControllerTest {
     private static final float BRIGHTNESS_MIN_FLOAT = 0.0f;
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java
index 6a6cd6c..800f60b 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java
@@ -35,7 +35,6 @@
 import android.os.Temperature;
 import android.os.Temperature.ThrottlingStatus;
 import android.os.test.TestLooper;
-import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -57,7 +56,6 @@
 import java.util.List;
 
 @SmallTest
-@Presubmit
 @RunWith(AndroidJUnit4.class)
 public class BrightnessThrottlerTest {
     private static final float EPSILON = 0.000001f;
diff --git a/services/tests/servicestests/src/com/android/server/display/ColorFadeTest.java b/services/tests/servicestests/src/com/android/server/display/ColorFadeTest.java
index 26a83a2..53d8de0c 100644
--- a/services/tests/servicestests/src/com/android/server/display/ColorFadeTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/ColorFadeTest.java
@@ -24,7 +24,6 @@
 
 import android.content.Context;
 import android.hardware.display.DisplayManagerInternal;
-import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -39,7 +38,6 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
-@Presubmit
 @RunWith(AndroidJUnit4.class)
 public class ColorFadeTest {
     private static final int DISPLAY_ID = 123;
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
index 66420ad..04702c4 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -27,7 +27,6 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
-import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -44,7 +43,6 @@
 import java.nio.file.Path;
 
 @SmallTest
-@Presubmit
 @RunWith(AndroidJUnit4.class)
 public final class DisplayDeviceConfigTest {
     private DisplayDeviceConfig mDisplayDeviceConfig;
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index 3eb1dea..3c7bb2a 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -53,12 +53,10 @@
 import android.hardware.display.IDisplayManagerCallback;
 import android.hardware.display.IVirtualDisplayCallback;
 import android.hardware.display.VirtualDisplayConfig;
-import android.hardware.input.InputManagerInternal;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.MessageQueue;
 import android.os.Process;
-import android.platform.test.annotations.Presubmit;
 import android.view.Display;
 import android.view.DisplayCutout;
 import android.view.DisplayEventReceiver;
@@ -78,6 +76,7 @@
 import com.android.server.SystemService;
 import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
 import com.android.server.display.DisplayManagerService.SyncRoot;
+import com.android.server.input.InputManagerInternal;
 import com.android.server.lights.LightsManager;
 import com.android.server.sensors.SensorManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
@@ -106,7 +105,6 @@
 import java.util.stream.LongStream;
 
 @SmallTest
-@Presubmit
 @RunWith(AndroidJUnit4.class)
 public class DisplayManagerServiceTest {
     private static final int MSG_REGISTER_DEFAULT_DISPLAY_ADAPTERS = 1;
diff --git a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
index 53fa3e2..a1e5ce7 100644
--- a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
@@ -22,14 +22,10 @@
 import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF;
 import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT;
 
-
 import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED;
 import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED;
-import static com.android.server.display.AutomaticBrightnessController
-                                                      .AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE;
-
+import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE;
 import static com.android.server.display.DisplayDeviceConfig.HDR_PERCENT_OF_SCREEN_REQUIRED_DEFAULT;
-
 import static com.android.server.display.HighBrightnessModeController.HBM_TRANSITION_POINT_INVALID;
 
 import static org.junit.Assert.assertEquals;
@@ -51,7 +47,6 @@
 import android.os.Temperature;
 import android.os.Temperature.ThrottlingStatus;
 import android.os.test.TestLooper;
-import android.platform.test.annotations.Presubmit;
 import android.test.mock.MockContentResolver;
 import android.util.MathUtils;
 
@@ -76,7 +71,6 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
-@Presubmit
 @RunWith(AndroidJUnit4.class)
 public class HighBrightnessModeControllerTest {
 
diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
index cc68ba8..0b33c30 100644
--- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
@@ -45,7 +45,6 @@
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.test.TestLooper;
-import android.platform.test.annotations.Presubmit;
 import android.view.Display;
 import android.view.DisplayAddress;
 import android.view.DisplayInfo;
@@ -67,7 +66,6 @@
 import java.util.Set;
 
 @SmallTest
-@Presubmit
 @RunWith(AndroidJUnit4.class)
 public class LogicalDisplayMapperTest {
     private static int sUniqueTestDisplayId = 0;
diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
index b0738fd..5a43530 100644
--- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
@@ -26,7 +26,6 @@
 
 import android.app.PropertyInvalidatedCache;
 import android.graphics.Point;
-import android.platform.test.annotations.Presubmit;
 import android.view.DisplayInfo;
 import android.view.Surface;
 import android.view.SurfaceControl;
@@ -40,7 +39,6 @@
 import java.io.OutputStream;
 
 @SmallTest
-@Presubmit
 public class LogicalDisplayTest {
     private static final int DISPLAY_ID = 0;
     private static final int LAYER_STACK = 0;
diff --git a/services/tests/servicestests/src/com/android/server/display/TEST_MAPPING b/services/tests/servicestests/src/com/android/server/display/TEST_MAPPING
index 9f1a209..92d8abd 100644
--- a/services/tests/servicestests/src/com/android/server/display/TEST_MAPPING
+++ b/services/tests/servicestests/src/com/android/server/display/TEST_MAPPING
@@ -3,18 +3,10 @@
     {
       "name": "FrameworksServicesTests",
       "options": [
-        {
-          "include-filter": "com.android.server.display."
-        },
-        {
-          "include-annotation": "android.platform.test.annotations.Presubmit"
-        },
-        {
-          "exclude-annotation": "androidx.test.filters.FlakyTest"
-        },
-        {
-          "exclude-annotation": "org.junit.Ignore"
-        }
+        {"include-filter": "com.android.server.display"},
+        {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
+        {"exclude-annotation": "androidx.test.filters.FlakyTest"},
+        {"exclude-annotation": "org.junit.Ignore"}
       ]
     }
   ]
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessEventTest.java b/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessEventTest.java
index f69c5c2..fabf535 100644
--- a/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessEventTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessEventTest.java
@@ -19,7 +19,6 @@
 import static org.junit.Assert.assertEquals;
 
 import android.hardware.display.BrightnessInfo;
-import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -29,7 +28,6 @@
 import org.junit.runner.RunWith;
 
 @SmallTest
-@Presubmit
 @RunWith(AndroidJUnit4.class)
 public final class BrightnessEventTest {
     private BrightnessEvent mBrightnessEvent;
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessReasonTest.java b/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessReasonTest.java
index ffc2e0d..57aa61a 100644
--- a/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessReasonTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessReasonTest.java
@@ -18,7 +18,6 @@
 
 import static org.junit.Assert.assertEquals;
 
-import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -28,7 +27,6 @@
 import org.junit.runner.RunWith;
 
 @SmallTest
-@Presubmit
 @RunWith(AndroidJUnit4.class)
 public final class BrightnessReasonTest {
     private BrightnessReason mBrightnessReason;
diff --git a/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt b/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt
index 844f5d4..e390bcc 100644
--- a/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt
+++ b/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt
@@ -19,13 +19,14 @@
 import android.content.Context
 import android.content.ContextWrapper
 import android.hardware.display.DisplayViewport
-import android.hardware.input.InputManagerInternal
 import android.os.IInputConstants
 import android.os.test.TestLooper
 import android.platform.test.annotations.Presubmit
 import android.view.Display
 import android.view.PointerIcon
 import androidx.test.InstrumentationRegistry
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
 import org.junit.Before
@@ -35,7 +36,6 @@
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.ArgumentMatchers.eq
 import org.mockito.Mock
-import org.mockito.Mockito.`when`
 import org.mockito.Mockito.clearInvocations
 import org.mockito.Mockito.doAnswer
 import org.mockito.Mockito.never
@@ -43,9 +43,8 @@
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.Mockito.`when`
 import org.mockito.junit.MockitoJUnit
-import java.util.concurrent.CountDownLatch
-import java.util.concurrent.TimeUnit
 
 /**
  * Tests for {@link InputManagerService}.
diff --git a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
index 90b19a4..04ba7d3 100644
--- a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
@@ -35,13 +35,13 @@
 import android.hardware.Sensor;
 import android.hardware.SensorEvent;
 import android.hardware.SensorManager;
-import android.hardware.input.InputManagerInternal;
 
 import androidx.annotation.NonNull;
 
 import com.android.server.LocalServices;
 import com.android.server.devicestate.DeviceState;
 import com.android.server.devicestate.DeviceStateProvider;
+import com.android.server.input.InputManagerInternal;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index 2918365..107bbe1 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -79,7 +79,10 @@
         <activity android:name="com.android.server.wm.ActivityOptionsTest$MainActivity"
                   android:turnScreenOn="true"
                   android:showWhenLocked="true" />
-        <activity android:name="com.android.server.wm.ScreenshotTests$ScreenshotActivity" />
+        <activity android:name="com.android.server.wm.ScreenshotTests$ScreenshotActivity"
+                  android:theme="@style/WhiteBackgroundTheme"
+                  android:turnScreenOn="true"
+                  android:showWhenLocked="true"/>
         <activity android:name="android.view.cts.surfacevalidator.CapturedActivity"/>
 
         <service android:name="android.view.cts.surfacevalidator.LocalMediaProjectionService"
diff --git a/services/tests/wmtests/res/values/styles.xml b/services/tests/wmtests/res/values/styles.xml
new file mode 100644
index 0000000..6857ff99
--- /dev/null
+++ b/services/tests/wmtests/res/values/styles.xml
@@ -0,0 +1,24 @@
+<!--
+  ~ Copyright (C) 2022 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.
+  -->
+
+<resources>
+    <style name="WhiteBackgroundTheme" parent="@android:style/Theme.DeviceDefault">
+        <item name="android:windowNoTitle">true</item>
+        <item name="android:windowFullscreen">true</item>
+        <item name="android:windowIsTranslucent">true</item>
+        <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
+    </style>
+</resources>
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index fe46c14..a76b82b 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -58,7 +58,6 @@
 import android.content.pm.PackageManager;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManagerInternal;
-import android.hardware.input.InputManagerInternal;
 import android.media.AudioManagerInternal;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -76,6 +75,7 @@
 import com.android.internal.accessibility.AccessibilityShortcutController;
 import com.android.server.GestureLauncherService;
 import com.android.server.LocalServices;
+import com.android.server.input.InputManagerInternal;
 import com.android.server.statusbar.StatusBarManagerInternal;
 import com.android.server.vr.VrManagerInternal;
 import com.android.server.wm.ActivityTaskManagerInternal;
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 333be7b..2f23e7f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2990,7 +2990,8 @@
                 .setSystemDecorations(true).build();
         // Add a decor insets provider window.
         final WindowState navbar = createNavBarWithProvidedInsets(squareDisplay);
-        squareDisplay.getDisplayPolicy().updateDecorInsetsInfoIfNeeded(navbar);
+        assertTrue(navbar.providesNonDecorInsets()
+                && squareDisplay.getDisplayPolicy().updateDecorInsetsInfo());
         squareDisplay.sendNewConfiguration();
         final Task task = new TaskBuilder(mSupervisor).setDisplay(squareDisplay).build();
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
index e2c94c5..49fd1ab 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
@@ -296,6 +296,14 @@
     }
 
     @Test
+    public void testRemoveTask_stopsRecording_nullSessionShouldNotThrowExceptions() {
+        mContentRecorder.setContentRecordingSession(mTaskSession);
+        mContentRecorder.updateRecording();
+        mContentRecorder.setContentRecordingSession(null);
+        mTask.removeImmediately();
+    }
+
+    @Test
     public void testUpdateMirroredSurface_capturedAreaResized() {
         mContentRecorder.setContentRecordingSession(mDisplaySession);
         mContentRecorder.updateRecording();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index 262b141..a980765 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -292,12 +292,16 @@
         final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
         final DisplayInfo di = mDisplayContent.getDisplayInfo();
         final int prevScreenHeightDp = mDisplayContent.getConfiguration().screenHeightDp;
-        assertTrue(displayPolicy.updateDecorInsetsInfoIfNeeded(navbar));
+        assertTrue(navbar.providesNonDecorInsets() && displayPolicy.updateDecorInsetsInfo());
         assertEquals(NAV_BAR_HEIGHT, displayPolicy.getDecorInsetsInfo(di.rotation,
                 di.logicalWidth, di.logicalHeight).mConfigInsets.bottom);
         mDisplayContent.sendNewConfiguration();
         assertNotEquals(prevScreenHeightDp, mDisplayContent.getConfiguration().screenHeightDp);
-        assertFalse(displayPolicy.updateDecorInsetsInfoIfNeeded(navbar));
+        assertFalse(navbar.providesNonDecorInsets() && displayPolicy.updateDecorInsetsInfo());
+
+        navbar.removeIfPossible();
+        assertEquals(0, displayPolicy.getDecorInsetsInfo(di.rotation, di.logicalWidth,
+                di.logicalHeight).mNonDecorInsets.bottom);
     }
 
     @SetupWindows(addWindows = { W_NAVIGATION_BAR, W_INPUT_METHOD })
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index adf694c..170b388 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -1532,7 +1532,7 @@
 
         @Override
         void getTasks(int maxNum, List<RunningTaskInfo> list, int flags, RecentTasks recentTasks,
-                WindowContainer root, int callingUid, ArraySet<Integer> profileIds) {
+                WindowContainer<?> root, int callingUid, ArraySet<Integer> profileIds) {
             mLastAllowed = (flags & FLAG_ALLOWED) == FLAG_ALLOWED;
             super.getTasks(maxNum, list, flags, recentTasks, root, callingUid, profileIds);
         }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java b/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java
index 0b58428..736f8f7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java
@@ -16,6 +16,10 @@
 
 package com.android.server.wm;
 
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowInsets.Type.displayCutout;
+import static android.view.WindowInsets.Type.statusBars;
+
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
 import static org.junit.Assert.assertNotNull;
@@ -23,20 +27,33 @@
 
 import android.app.Activity;
 import android.app.Instrumentation;
+import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
-import android.graphics.ColorSpace;
 import android.graphics.GraphicBuffer;
+import android.graphics.Insets;
 import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.hardware.DataSpace;
+import android.hardware.HardwareBuffer;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.ServiceManager;
 import android.platform.test.annotations.Presubmit;
+import android.util.Pair;
+import android.view.IWindowManager;
 import android.view.PointerIcon;
 import android.view.SurfaceControl;
-import android.view.WindowManager;
+import android.view.cts.surfacevalidator.BitmapPixelChecker;
+import android.view.cts.surfacevalidator.PixelColor;
+import android.view.cts.surfacevalidator.SaveBitmapHelper;
 import android.window.ScreenCapture;
+import android.window.ScreenCapture.ScreenCaptureListener;
+import android.window.ScreenCapture.ScreenshotHardwareBuffer;
+import android.window.ScreenCapture.ScreenshotSync;
 
 import androidx.annotation.Nullable;
 import androidx.test.filters.SmallTest;
@@ -45,6 +62,7 @@
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TestName;
 
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -60,6 +78,8 @@
     private static final int BUFFER_HEIGHT = 100;
 
     private final Instrumentation mInstrumentation = getInstrumentation();
+    @Rule
+    public TestName mTestName = new TestName();
 
     @Rule
     public ActivityTestRule<ScreenshotActivity> mActivityRule =
@@ -95,8 +115,8 @@
         buffer.unlockCanvasAndPost(canvas);
 
         t.show(secureSC)
-                .setBuffer(secureSC, buffer)
-                .setColorSpace(secureSC, ColorSpace.get(ColorSpace.Named.SRGB))
+                .setBuffer(secureSC, HardwareBuffer.createFromGraphicBuffer(buffer))
+                .setDataSpace(secureSC, DataSpace.DATASPACE_SRGB)
                 .apply(true);
 
         ScreenCapture.LayerCaptureArgs args = new ScreenCapture.LayerCaptureArgs.Builder(secureSC)
@@ -112,15 +132,70 @@
         Bitmap swBitmap = screenshot.copy(Bitmap.Config.ARGB_8888, false);
         screenshot.recycle();
 
-        int numMatchingPixels = PixelChecker.getNumMatchingPixels(swBitmap,
-                new PixelColor(PixelColor.RED));
-        long sizeOfBitmap = swBitmap.getWidth() * swBitmap.getHeight();
+        BitmapPixelChecker bitmapPixelChecker = new BitmapPixelChecker(PixelColor.RED);
+        Rect bounds = new Rect(0, 0, swBitmap.getWidth(), swBitmap.getHeight());
+        int numMatchingPixels = bitmapPixelChecker.getNumMatchingPixels(swBitmap, bounds);
+        int sizeOfBitmap = bounds.width() * bounds.height();
         boolean success = numMatchingPixels == sizeOfBitmap;
         swBitmap.recycle();
 
         assertTrue(success);
     }
 
+    @Test
+    public void testCaptureDisplay() throws Exception {
+        IWindowManager windowManager = IWindowManager.Stub.asInterface(
+                ServiceManager.getService(Context.WINDOW_SERVICE));
+        SurfaceControl sc = new SurfaceControl.Builder()
+                .setName("Layer")
+                .setCallsite("testCaptureDisplay")
+                .build();
+
+        SurfaceControl.Transaction t = mActivity.addChildSc(sc);
+        mInstrumentation.waitForIdleSync();
+
+        GraphicBuffer buffer = GraphicBuffer.create(BUFFER_WIDTH, BUFFER_HEIGHT,
+                PixelFormat.RGBA_8888,
+                GraphicBuffer.USAGE_HW_TEXTURE | GraphicBuffer.USAGE_HW_COMPOSER
+                        | GraphicBuffer.USAGE_SW_WRITE_RARELY);
+
+        Canvas canvas = buffer.lockCanvas();
+        canvas.drawColor(Color.RED);
+        buffer.unlockCanvasAndPost(canvas);
+
+        Point point = mActivity.getPositionBelowStatusBar();
+        t.show(sc)
+                .setBuffer(sc, HardwareBuffer.createFromGraphicBuffer(buffer))
+                .setDataSpace(sc, DataSpace.DATASPACE_SRGB)
+                .setPosition(sc, point.x, point.y)
+                .apply(true);
+
+        Pair<ScreenCaptureListener, ScreenshotSync> syncScreenCapture =
+                ScreenCapture.createSyncCaptureListener();
+        windowManager.captureDisplay(DEFAULT_DISPLAY, null, syncScreenCapture.first);
+        ScreenshotHardwareBuffer hardwareBuffer = syncScreenCapture.second.get();
+        assertNotNull(hardwareBuffer);
+
+        Bitmap screenshot = hardwareBuffer.asBitmap();
+        assertNotNull(screenshot);
+
+        Bitmap swBitmap = screenshot.copy(Bitmap.Config.ARGB_8888, false);
+        screenshot.recycle();
+
+        BitmapPixelChecker bitmapPixelChecker = new BitmapPixelChecker(PixelColor.RED);
+        Rect bounds = new Rect(point.x, point.y, BUFFER_WIDTH + point.x, BUFFER_HEIGHT + point.y);
+        int numMatchingPixels = bitmapPixelChecker.getNumMatchingPixels(swBitmap, bounds);
+        int pixelMatchSize = bounds.width() * bounds.height();
+        boolean success = numMatchingPixels == pixelMatchSize;
+
+        if (!success) {
+            SaveBitmapHelper.saveBitmap(swBitmap, getClass(), mTestName, "failedImage");
+        }
+        swBitmap.recycle();
+        assertTrue("numMatchingPixels=" + numMatchingPixels + " pixelMatchSize=" + pixelMatchSize,
+                success);
+    }
+
     public static class ScreenshotActivity extends Activity {
         private static final long WAIT_TIMEOUT_S = 5;
         private final Handler mHandler = new Handler(Looper.getMainLooper());
@@ -130,7 +205,6 @@
             super.onCreate(savedInstanceState);
             getWindow().getDecorView().setPointerIcon(
                     PointerIcon.getSystemIcon(this, PointerIcon.TYPE_NULL));
-            getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
         }
 
         SurfaceControl.Transaction addChildSc(SurfaceControl surfaceControl) {
@@ -148,88 +222,14 @@
             }
             return t;
         }
-    }
 
-    public abstract static class PixelChecker {
-        static int getNumMatchingPixels(Bitmap bitmap, PixelColor pixelColor) {
-            int numMatchingPixels = 0;
-            for (int x = 0; x < bitmap.getWidth(); x++) {
-                for (int y = 0; y < bitmap.getHeight(); y++) {
-                    int color = bitmap.getPixel(x, y);
-                    if (matchesColor(pixelColor, color)) {
-                        numMatchingPixels++;
-                    }
-                }
-            }
-            return numMatchingPixels;
-        }
+        public Point getPositionBelowStatusBar() {
+            Insets statusBarInsets = getWindow()
+                    .getDecorView()
+                    .getRootWindowInsets()
+                    .getInsets(statusBars() | displayCutout());
 
-        static boolean matchesColor(PixelColor expectedColor, int color) {
-            final float red = Color.red(color);
-            final float green = Color.green(color);
-            final float blue = Color.blue(color);
-            final float alpha = Color.alpha(color);
-
-            return alpha <= expectedColor.mMaxAlpha
-                    && alpha >= expectedColor.mMinAlpha
-                    && red <= expectedColor.mMaxRed
-                    && red >= expectedColor.mMinRed
-                    && green <= expectedColor.mMaxGreen
-                    && green >= expectedColor.mMinGreen
-                    && blue <= expectedColor.mMaxBlue
-                    && blue >= expectedColor.mMinBlue;
-        }
-    }
-
-    public static class PixelColor {
-        public static final int BLACK = 0xFF000000;
-        public static final int RED = 0xFF0000FF;
-        public static final int GREEN = 0xFF00FF00;
-        public static final int BLUE = 0xFFFF0000;
-        public static final int YELLOW = 0xFF00FFFF;
-        public static final int MAGENTA = 0xFFFF00FF;
-        public static final int WHITE = 0xFFFFFFFF;
-
-        public static final int TRANSPARENT_RED = 0x7F0000FF;
-        public static final int TRANSPARENT_BLUE = 0x7FFF0000;
-        public static final int TRANSPARENT = 0x00000000;
-
-        // Default to black
-        public short mMinAlpha;
-        public short mMaxAlpha;
-        public short mMinRed;
-        public short mMaxRed;
-        public short mMinBlue;
-        public short mMaxBlue;
-        public short mMinGreen;
-        public short mMaxGreen;
-
-        public PixelColor(int color) {
-            short alpha = (short) ((color >> 24) & 0xFF);
-            short blue = (short) ((color >> 16) & 0xFF);
-            short green = (short) ((color >> 8) & 0xFF);
-            short red = (short) (color & 0xFF);
-
-            mMinAlpha = (short) getMinValue(alpha);
-            mMaxAlpha = (short) getMaxValue(alpha);
-            mMinRed = (short) getMinValue(red);
-            mMaxRed = (short) getMaxValue(red);
-            mMinBlue = (short) getMinValue(blue);
-            mMaxBlue = (short) getMaxValue(blue);
-            mMinGreen = (short) getMinValue(green);
-            mMaxGreen = (short) getMaxValue(green);
-        }
-
-        public PixelColor() {
-            this(BLACK);
-        }
-
-        private int getMinValue(short color) {
-            return Math.max(color - 4, 0);
-        }
-
-        private int getMaxValue(short color) {
-            return Math.min(color + 4, 0xFF);
+            return new Point(statusBarInsets.left, statusBarInsets.top);
         }
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index 46b4b76..8b63904 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -41,6 +41,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.mockito.ArgumentMatchers.any;
@@ -68,6 +69,7 @@
 import android.view.View;
 import android.view.WindowManager;
 import android.window.ClientWindowFrames;
+import android.window.ScreenCapture;
 import android.window.WindowContainerToken;
 
 import androidx.test.filters.SmallTest;
@@ -423,6 +425,45 @@
                 LETTERBOX_BACKGROUND_SOLID_COLOR)).isFalse();
     }
 
+    @Test
+    public void testCaptureDisplay() {
+        Rect displayBounds = new Rect(0, 0, 100, 200);
+        spyOn(mDisplayContent);
+        when(mDisplayContent.getBounds()).thenReturn(displayBounds);
+
+        // Null captureArgs
+        ScreenCapture.LayerCaptureArgs resultingArgs =
+                mWm.getCaptureArgs(DEFAULT_DISPLAY, null /* captureArgs */);
+        assertEquals(displayBounds, resultingArgs.mSourceCrop);
+
+        // Non null captureArgs, didn't set rect
+        ScreenCapture.CaptureArgs captureArgs = new ScreenCapture.CaptureArgs.Builder<>().build();
+        resultingArgs = mWm.getCaptureArgs(DEFAULT_DISPLAY, captureArgs);
+        assertEquals(displayBounds, resultingArgs.mSourceCrop);
+
+        // Non null captureArgs, invalid rect
+        captureArgs = new ScreenCapture.CaptureArgs.Builder<>()
+                .setSourceCrop(new Rect(0, 0, -1, -1))
+                .build();
+        resultingArgs = mWm.getCaptureArgs(DEFAULT_DISPLAY, captureArgs);
+        assertEquals(displayBounds, resultingArgs.mSourceCrop);
+
+        // Non null captureArgs, null rect
+        captureArgs = new ScreenCapture.CaptureArgs.Builder<>()
+                .setSourceCrop(null)
+                .build();
+        resultingArgs = mWm.getCaptureArgs(DEFAULT_DISPLAY, captureArgs);
+        assertEquals(displayBounds, resultingArgs.mSourceCrop);
+
+        // Non null captureArgs, valid rect
+        Rect validRect = new Rect(0, 0, 10, 50);
+        captureArgs = new ScreenCapture.CaptureArgs.Builder<>()
+                .setSourceCrop(validRect)
+                .build();
+        resultingArgs = mWm.getCaptureArgs(DEFAULT_DISPLAY, captureArgs);
+        assertEquals(validRect, resultingArgs.mSourceCrop);
+    }
+
     private void setupActivityWithLaunchCookie(IBinder launchCookie, WindowContainerToken wct) {
         final WindowContainer.RemoteToken remoteToken = mock(WindowContainer.RemoteToken.class);
         when(remoteToken.toWindowContainerToken()).thenReturn(wct);
diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java
index 1d6798b..a0467c2 100644
--- a/telephony/java/android/telephony/NetworkRegistrationInfo.java
+++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java
@@ -856,6 +856,31 @@
         public Builder() {}
 
         /**
+         * Builder from the existing {@link NetworkRegistrationInfo}.
+         *
+         * @param nri The network registration info object.
+         * @hide
+         */
+        public Builder(@NonNull NetworkRegistrationInfo nri) {
+            mDomain = nri.mDomain;
+            mTransportType = nri.mTransportType;
+            mInitialRegistrationState = nri.mInitialRegistrationState;
+            mAccessNetworkTechnology = nri.mAccessNetworkTechnology;
+            mRejectCause = nri.mRejectCause;
+            mEmergencyOnly = nri.mEmergencyOnly;
+            mAvailableServices = new ArrayList<>(nri.mAvailableServices);
+            mCellIdentity = nri.mCellIdentity;
+            if (nri.mDataSpecificInfo != null) {
+                mDataSpecificRegistrationInfo = new DataSpecificRegistrationInfo(
+                        nri.mDataSpecificInfo);
+            }
+            if (nri.mVoiceSpecificInfo != null) {
+                mVoiceSpecificRegistrationInfo = new VoiceSpecificRegistrationInfo(
+                        nri.mVoiceSpecificInfo);
+            }
+        }
+
+        /**
          * Set the network domain.
          *
          * @param domain Network domain.
diff --git a/tests/Codegen/Android.bp b/tests/Codegen/Android.bp
index ddbf168..7fbe3b3 100644
--- a/tests/Codegen/Android.bp
+++ b/tests/Codegen/Android.bp
@@ -24,6 +24,14 @@
     plugins: [
         "staledataclass-annotation-processor",
     ],
+    // Exports needed for staledataclass-annotation-processor, see b/139342589.
+    javacflags: [
+        "-J--add-modules=jdk.compiler",
+        "-J--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
+        "-J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED",
+        "-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
+        "-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
+    ],
     static_libs: [
         "junit",
         "hamcrest",
diff --git a/tests/HandwritingIme/OWNERS b/tests/HandwritingIme/OWNERS
new file mode 100644
index 0000000..6bb4b17
--- /dev/null
+++ b/tests/HandwritingIme/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 34867
+
+include /services/core/java/com/android/server/inputmethod/OWNERS
diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt b/tools/lint/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt
index a415217..bba819c 100644
--- a/tools/lint/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt
+++ b/tools/lint/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt
@@ -82,8 +82,16 @@
             if (attr1[i].name != attr2[i].name) {
                 return false
             }
-            val v1 = ConstantEvaluator.evaluate(context, attr1[i].value)
-            val v2 = ConstantEvaluator.evaluate(context, attr2[i].value)
+            val value1 = attr1[i].value
+            val value2 = attr2[i].value
+            if (value1 == null && value2 == null) {
+                continue
+            }
+            if (value1 == null || value2 == null) {
+                return false
+            }
+            val v1 = ConstantEvaluator.evaluate(context, value1)
+            val v2 = ConstantEvaluator.evaluate(context, value2)
             if (v1 != v2) {
                 return false
             }
diff --git a/tools/processors/staledataclass/Android.bp b/tools/processors/staledataclass/Android.bp
index 1e50976..2169c49 100644
--- a/tools/processors/staledataclass/Android.bp
+++ b/tools/processors/staledataclass/Android.bp
@@ -22,17 +22,13 @@
     static_libs: [
         "codegen-version-info",
     ],
-    // The --add-modules/exports flags below don't work for kotlinc yet, so pin this module to Java language level 8 (see b/139342589):
-    java_version: "1.8",
-    openjdk9: {
-        javacflags: [
-            "--add-modules=jdk.compiler",
-            "--add-exports jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
-            "--add-exports jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED",
-            "--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
-            "--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
-        ],
-    },
+    javacflags: [
+        "--add-modules=jdk.compiler",
+        "--add-exports jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
+        "--add-exports jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED",
+        "--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
+        "--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
+    ],
 
     use_tools_jar: true,
 }
diff --git a/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt b/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt
index 27a8853..1cef5b0 100644
--- a/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt
+++ b/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+@file:Suppress("JAVA_MODULE_DOES_NOT_EXPORT_PACKAGE")
 
 package android.processor.staledataclass