[automerger skipped] Merge "Don't set screen state until boot is completed" into tm-qpr-dev am: 63d87dcbe9 am: 8e2e91c411 -s ours am: 7f029edeac -s ours am: b550565006 -s ours

am skip reason: skipped by user wilczynskip

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21264319

Change-Id: Idb5ca4f9682f46364ab4c2b36c3c0a6cccbb3ab9
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/apct-tests/perftests/core/src/android/input/VelocityTrackerBenchmarkTest.kt b/apct-tests/perftests/core/src/android/input/VelocityTrackerBenchmarkTest.kt
new file mode 100644
index 0000000..324fcc6
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/input/VelocityTrackerBenchmarkTest.kt
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2023 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.input
+
+import android.perftests.utils.PerfStatusReporter
+import android.view.InputDevice
+import android.view.MotionEvent
+import android.view.VelocityTracker
+
+import androidx.test.filters.LargeTest
+import androidx.test.runner.AndroidJUnit4
+
+import org.junit.Assert
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+private fun createScrollMotionEvent(scrollAmount: Float, eventTimeMs: Long): MotionEvent {
+    val props = MotionEvent.PointerProperties()
+    props.id = 0
+    val coords = MotionEvent.PointerCoords()
+    coords.setAxisValue(MotionEvent.AXIS_SCROLL, scrollAmount)
+    return MotionEvent.obtain(
+        /*downTime=*/0,
+        eventTimeMs,
+        MotionEvent.ACTION_SCROLL,
+        /*pointerCount=*/1,
+        arrayOf(props),
+        arrayOf(coords),
+        /*metaState=*/0,
+        /*buttonState=*/0,
+        /*xPrecision=*/0f,
+        /*yPrecision=*/0f,
+        /*deviceId=*/1,
+        /*edgeFlags=*/0,
+        InputDevice.SOURCE_ROTARY_ENCODER,
+        /*flags=*/0
+    )
+}
+
+/**
+ * Benchmark tests for [VelocityTracker]
+ *
+ * Build/Install/Run:
+ * atest VelocityTrackerBenchmarkTest
+ */
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+class VelocityTrackerBenchmarkTest {
+    @get:Rule
+    val perfStatusReporter: PerfStatusReporter = PerfStatusReporter()
+
+    private val velocityTracker = VelocityTracker.obtain()
+    @Before
+    fun setup() {
+        velocityTracker.clear()
+    }
+
+    @Test
+    fun addMovement_axisScroll() {
+        val state = perfStatusReporter.getBenchmarkState()
+        while (state.keepRunning()) {
+            state.pauseTiming()
+            var eventTimeMs: Long = 100
+            val scrollAmount = 30f
+            for (i in 0 until TEST_NUM_DATAPOINTS) {
+                state.resumeTiming()
+                velocityTracker.addMovement(createScrollMotionEvent(scrollAmount, eventTimeMs))
+                state.pauseTiming()
+                eventTimeMs += 2
+            }
+            velocityTracker.computeCurrentVelocity(1000)
+            Assert.assertTrue(velocityTracker.getAxisVelocity(MotionEvent.AXIS_SCROLL) > 0)
+            // Clear the tracker for the next run
+            velocityTracker.clear()
+            state.resumeTiming()
+        }
+    }
+
+    @Test
+    fun computeCurrentVelocity_constantVelocity_axisScroll_computeAfterAllAdditions() {
+        val state = perfStatusReporter.getBenchmarkState()
+        while (state.keepRunning()) {
+            // Add the data points
+            state.pauseTiming()
+            var eventTimeMs: Long = 100
+            val scrollAmount = 30f
+            for (i in 0 until TEST_NUM_DATAPOINTS) {
+                velocityTracker.addMovement(createScrollMotionEvent(scrollAmount, eventTimeMs))
+                eventTimeMs += 2
+            }
+
+            // Do the velocity computation
+            state.resumeTiming()
+            velocityTracker.computeCurrentVelocity(1000)
+
+            // Clear the tracker for the next run
+            state.pauseTiming()
+            Assert.assertTrue(velocityTracker.getAxisVelocity(MotionEvent.AXIS_SCROLL) > 0)
+            velocityTracker.clear()
+            state.resumeTiming()
+        }
+    }
+
+    @Test
+    fun computeCurrentVelocity_constantVelocity_axisScroll_computeAfterEachAdd() {
+        val state = perfStatusReporter.getBenchmarkState()
+        while (state.keepRunning()) {
+            state.pauseTiming()
+            var eventTimeMs: Long = 100
+            val scrollAmount = 30f
+            for (i in 0 until TEST_NUM_DATAPOINTS) {
+                velocityTracker.addMovement(createScrollMotionEvent(scrollAmount, eventTimeMs))
+                state.resumeTiming()
+                velocityTracker.computeCurrentVelocity(1000)
+                state.pauseTiming()
+                eventTimeMs += 2
+            }
+            Assert.assertTrue(velocityTracker.getAxisVelocity(MotionEvent.AXIS_SCROLL) > 0)
+            // Clear the tracker for the next run
+            velocityTracker.clear()
+            state.resumeTiming()
+        }
+    }
+
+    companion object {
+        private const val TEST_NUM_DATAPOINTS = 100
+    }
+}
\ No newline at end of file
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
index 051dde0..4c2cf2b 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
@@ -1554,8 +1554,9 @@
     }
 
     private void waitCoolDownPeriod() {
-        final int tenSeconds = 1000 * 10;
+        // Heuristic value based on local tests. Stability increased compared to no waiting.
+        final int fiveSeconds = 1000 * 5;
         waitForBroadcastIdle();
-        sleep(tenSeconds);
+        sleep(fiveSeconds);
     }
 }
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index e2fff0f..deb8a63 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -766,11 +766,11 @@
          * Default quota for pre-S apps. The same as allowing an alarm slot once
          * every ALLOW_WHILE_IDLE_LONG_DELAY, which was 9 minutes.
          */
-        private static final int DEFAULT_ALLOW_WHILE_IDLE_COMPAT_QUOTA = 1;
+        private static final int DEFAULT_ALLOW_WHILE_IDLE_COMPAT_QUOTA = 7;
         private static final int DEFAULT_ALLOW_WHILE_IDLE_QUOTA = 72;
 
         private static final long DEFAULT_ALLOW_WHILE_IDLE_WINDOW = 60 * 60 * 1000; // 1 hour.
-        private static final long DEFAULT_ALLOW_WHILE_IDLE_COMPAT_WINDOW = 9 * 60 * 1000; // 9 mins.
+        private static final long DEFAULT_ALLOW_WHILE_IDLE_COMPAT_WINDOW = 60 * 60 * 1000;
 
         private static final long DEFAULT_PRIORITY_ALARM_DELAY = 9 * 60_000;
 
diff --git a/core/api/current.txt b/core/api/current.txt
index eef16be..9f27457 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -5279,8 +5279,8 @@
   }
 
   public class BroadcastOptions {
+    ctor public BroadcastOptions();
     method public boolean isShareIdentityEnabled();
-    method @NonNull public static android.app.BroadcastOptions makeBasic();
     method @NonNull public android.app.BroadcastOptions setShareIdentityEnabled(boolean);
     method @NonNull public android.os.Bundle toBundle();
   }
@@ -32557,6 +32557,7 @@
     field public static final int S_V2 = 32; // 0x20
     field public static final int TIRAMISU = 33; // 0x21
     field public static final int UPSIDE_DOWN_CAKE = 10000; // 0x2710
+    field public static final int VANILLA_ICE_CREAM = 10000; // 0x2710
   }
 
   public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
@@ -41246,6 +41247,7 @@
     method public void setUiEnabled(boolean);
     method public void show(android.os.Bundle, int);
     method public void startAssistantActivity(android.content.Intent);
+    method public void startAssistantActivity(@NonNull android.content.Intent, @NonNull android.os.Bundle);
     method public void startVoiceActivity(android.content.Intent);
     method public final void unregisterVisibleActivityCallback(@NonNull android.service.voice.VoiceInteractionSession.VisibleActivityCallback);
     field public static final String KEY_SHOW_SESSION_ID = "android.service.voice.SHOW_SESSION_ID";
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 95e331e..701054f 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -842,6 +842,7 @@
     method public int getPendingIntentBackgroundActivityStartMode();
     method public boolean isDeferUntilActive();
     method @Deprecated public boolean isPendingIntentBackgroundActivityLaunchAllowed();
+    method @Deprecated @NonNull public static android.app.BroadcastOptions makeBasic();
     method @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RESPONSE_STATS) public void recordResponseEventWhileInBackground(@IntRange(from=0) long);
     method @RequiresPermission(android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND) public void setBackgroundActivityStartsAllowed(boolean);
     method @NonNull public android.app.BroadcastOptions setDeferUntilActive(boolean);
@@ -5117,7 +5118,7 @@
     method @NonNull public android.hardware.input.VirtualTouchEvent build();
     method @NonNull public android.hardware.input.VirtualTouchEvent.Builder setAction(int);
     method @NonNull public android.hardware.input.VirtualTouchEvent.Builder setMajorAxisSize(@FloatRange(from=0.0f) float);
-    method @NonNull public android.hardware.input.VirtualTouchEvent.Builder setPointerId(int);
+    method @NonNull public android.hardware.input.VirtualTouchEvent.Builder setPointerId(@IntRange(from=0, to=0x10 - 1) int);
     method @NonNull public android.hardware.input.VirtualTouchEvent.Builder setPressure(@FloatRange(from=0.0f) float);
     method @NonNull public android.hardware.input.VirtualTouchEvent.Builder setToolType(int);
     method @NonNull public android.hardware.input.VirtualTouchEvent.Builder setX(float);
@@ -10719,15 +10720,15 @@
 
   public static final class PowerManager.LowPowerStandbyPortDescription {
     ctor public PowerManager.LowPowerStandbyPortDescription(int, int, int);
-    ctor public PowerManager.LowPowerStandbyPortDescription(int, int, int, @Nullable android.net.LinkAddress);
-    method @Nullable public android.net.LinkAddress getBindAddress();
+    ctor public PowerManager.LowPowerStandbyPortDescription(int, int, int, @Nullable java.net.InetAddress);
+    method @Nullable public java.net.InetAddress getLocalAddress();
     method public int getPortMatcher();
     method public int getPortNumber();
     method public int getProtocol();
     field public static final int MATCH_PORT_LOCAL = 1; // 0x1
     field public static final int MATCH_PORT_REMOTE = 2; // 0x2
-    field public static final int PROTOCOL_TCP = 1; // 0x1
-    field public static final int PROTOCOL_UDP = 2; // 0x2
+    field public static final int PROTOCOL_TCP = 6; // 0x6
+    field public static final int PROTOCOL_UDP = 17; // 0x11
   }
 
   public final class PowerManager.LowPowerStandbyPortsLock {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index c0cd638..addb3e7 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -985,7 +985,7 @@
     method public boolean isInitialized();
     method public boolean isMain();
     method public boolean isManagedProfile();
-    method public boolean isPrimary();
+    method @Deprecated public boolean isPrimary();
     method public boolean isProfile();
     method public boolean isQuietModeEnabled();
     method public boolean isRestricted();
@@ -1002,7 +1002,7 @@
     field public static final int FLAG_INITIALIZED = 16; // 0x10
     field public static final int FLAG_MAIN = 16384; // 0x4000
     field @Deprecated public static final int FLAG_MANAGED_PROFILE = 32; // 0x20
-    field public static final int FLAG_PRIMARY = 1; // 0x1
+    field @Deprecated public static final int FLAG_PRIMARY = 1; // 0x1
     field public static final int FLAG_PROFILE = 4096; // 0x1000
     field public static final int FLAG_QUIET_MODE = 128; // 0x80
     field @Deprecated public static final int FLAG_RESTRICTED = 8; // 0x8
@@ -3294,6 +3294,7 @@
 
   public interface WindowManager extends android.view.ViewManager {
     method public default int getDisplayImePolicy(int);
+    method public static boolean hasWindowExtensionsEnabled();
     method public default void holdLock(android.os.IBinder, int);
     method public default boolean isGlobalKey(int);
     method public default boolean isTaskSnapshotSupported();
diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java
index a81ef18..322d23b 100644
--- a/core/java/android/animation/Animator.java
+++ b/core/java/android/animation/Animator.java
@@ -72,6 +72,13 @@
     private static long sBackgroundPauseDelay = 10000;
 
     /**
+     * A cache of the values in a list. Used so that when calling the list, we have a copy
+     * of it in case the list is modified while iterating. The array can be reused to avoid
+     * allocation on every notification.
+     */
+    private Object[] mCachedList;
+
+    /**
      * Sets the duration for delaying pausing animators when apps go into the background.
      * Used by AnimationHandler when requested to pause animators.
      *
@@ -160,14 +167,7 @@
     public void pause() {
         if (isStarted() && !mPaused) {
             mPaused = true;
-            if (mPauseListeners != null) {
-                ArrayList<AnimatorPauseListener> tmpListeners =
-                        (ArrayList<AnimatorPauseListener>) mPauseListeners.clone();
-                int numListeners = tmpListeners.size();
-                for (int i = 0; i < numListeners; ++i) {
-                    tmpListeners.get(i).onAnimationPause(this);
-                }
-            }
+            notifyPauseListeners(AnimatorCaller.ON_PAUSE);
         }
     }
 
@@ -184,14 +184,7 @@
     public void resume() {
         if (mPaused) {
             mPaused = false;
-            if (mPauseListeners != null) {
-                ArrayList<AnimatorPauseListener> tmpListeners =
-                        (ArrayList<AnimatorPauseListener>) mPauseListeners.clone();
-                int numListeners = tmpListeners.size();
-                for (int i = 0; i < numListeners; ++i) {
-                    tmpListeners.get(i).onAnimationResume(this);
-                }
-            }
+            notifyPauseListeners(AnimatorCaller.ON_RESUME);
         }
     }
 
@@ -450,6 +443,7 @@
             if (mPauseListeners != null) {
                 anim.mPauseListeners = new ArrayList<AnimatorPauseListener>(mPauseListeners);
             }
+            anim.mCachedList = null;
             return anim;
         } catch (CloneNotSupportedException e) {
            throw new AssertionError();
@@ -591,6 +585,70 @@
     }
 
     /**
+     * Calls notification for each AnimatorListener.
+     *
+     * @param notification The notification method to call on each listener.
+     * @param isReverse When this is used with start/end, this is the isReverse parameter. For
+     *                  other calls, this is ignored.
+     */
+    void notifyListeners(
+            AnimatorCaller<AnimatorListener, Animator> notification,
+            boolean isReverse
+    ) {
+        callOnList(mListeners, notification, this, isReverse);
+    }
+
+    /**
+     * Call pause/resume on each AnimatorPauseListener.
+     *
+     * @param notification Either ON_PAUSE or ON_RESUME to call onPause or onResume on each
+     *                     listener.
+     */
+    void notifyPauseListeners(AnimatorCaller<AnimatorPauseListener, Animator> notification) {
+        callOnList(mPauseListeners, notification, this, false);
+    }
+
+    /**
+     * Calls <code>call</code> for every item in <code>list</code> with <code>animator</code> and
+     * <code>isReverse</code> as parameters.
+     *
+     * @param list The list of items to make calls on.
+     * @param call The method to call for each item in list.
+     * @param animator The animator parameter of call.
+     * @param isReverse The isReverse parameter of call.
+     * @param <T> The item type of list
+     * @param <A> The Animator type of animator.
+     */
+    <T, A> void callOnList(
+            ArrayList<T> list,
+            AnimatorCaller<T, A> call,
+            A animator,
+            boolean isReverse
+    ) {
+        int size = list == null ? 0 : list.size();
+        if (size > 0) {
+            // Try to reuse mCacheList to store the items of list.
+            Object[] array;
+            if (mCachedList == null || mCachedList.length < size) {
+                array = new Object[size];
+            } else {
+                array = mCachedList;
+                // Clear it in case there is some reentrancy
+                mCachedList = null;
+            }
+            list.toArray(array);
+            for (int i = 0; i < size; i++) {
+                //noinspection unchecked
+                T item = (T) array[i];
+                call.call(item, animator, isReverse);
+                array[i] = null;
+            }
+            // Store it for the next call so we can reuse this array, if needed.
+            mCachedList = array;
+        }
+    }
+
+    /**
      * <p>An animation listener receives notifications from an animation.
      * Notifications indicate animation related events, such as the end or the
      * repetition of the animation.</p>
@@ -748,4 +806,29 @@
             return clone;
         }
     }
+
+    /**
+     * Internally used by {@link #callOnList(ArrayList, AnimatorCaller, Object, boolean)} to
+     * make a call on all children of a list. This can be for start, stop, pause, cancel, update,
+     * etc notifications.
+     *
+     * @param <T> The type of listener to make the call on
+     * @param <A> The type of animator that is passed as a parameter
+     */
+    interface AnimatorCaller<T, A> {
+        void call(T listener, A animator, boolean isReverse);
+
+        AnimatorCaller<AnimatorListener, Animator> ON_START = AnimatorListener::onAnimationStart;
+        AnimatorCaller<AnimatorListener, Animator> ON_END = AnimatorListener::onAnimationEnd;
+        AnimatorCaller<AnimatorListener, Animator> ON_CANCEL =
+                (listener, animator, isReverse) -> listener.onAnimationCancel(animator);
+        AnimatorCaller<AnimatorListener, Animator> ON_REPEAT =
+                (listener, animator, isReverse) -> listener.onAnimationRepeat(animator);
+        AnimatorCaller<AnimatorPauseListener, Animator> ON_PAUSE =
+                (listener, animator, isReverse) -> listener.onAnimationPause(animator);
+        AnimatorCaller<AnimatorPauseListener, Animator> ON_RESUME =
+                (listener, animator, isReverse) -> listener.onAnimationResume(animator);
+        AnimatorCaller<ValueAnimator.AnimatorUpdateListener, ValueAnimator> ON_UPDATE =
+                (listener, animator, isReverse) -> listener.onAnimationUpdate(animator);
+    }
 }
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index 257adfe..09eec9d 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -32,6 +32,7 @@
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
+import java.util.function.Consumer;
 
 /**
  * This class plays a set of {@link Animator} objects in the specified order. Animations
@@ -424,24 +425,28 @@
             throw new AndroidRuntimeException("Animators may only be run on Looper threads");
         }
         if (isStarted()) {
-            ArrayList<AnimatorListener> tmpListeners = null;
-            if (mListeners != null) {
-                tmpListeners = (ArrayList<AnimatorListener>) mListeners.clone();
-                int size = tmpListeners.size();
-                for (int i = 0; i < size; i++) {
-                    tmpListeners.get(i).onAnimationCancel(this);
-                }
-            }
-            ArrayList<Node> playingSet = new ArrayList<>(mPlayingSet);
-            int setSize = playingSet.size();
-            for (int i = 0; i < setSize; i++) {
-                playingSet.get(i).mAnimation.cancel();
-            }
+            notifyListeners(AnimatorCaller.ON_CANCEL, false);
+            callOnPlayingSet(Animator::cancel);
             mPlayingSet.clear();
             endAnimation();
         }
     }
 
+    /**
+     * Calls consumer on every Animator of mPlayingSet.
+     *
+     * @param consumer The method to call on every Animator of mPlayingSet.
+     */
+    private void callOnPlayingSet(Consumer<Animator> consumer) {
+        final ArrayList<Node> list = mPlayingSet;
+        final int size = list.size();
+        //noinspection ForLoopReplaceableByForEach
+        for (int i = 0; i < size; i++) {
+            final Animator animator = list.get(i).mAnimation;
+            consumer.accept(animator);
+        }
+    }
+
     // Force all the animations to end when the duration scale is 0.
     private void forceToEnd() {
         if (mEndCanBeCalled) {
@@ -662,6 +667,7 @@
         super.pause();
         if (!previouslyPaused && mPaused) {
             mPauseTime = -1;
+            callOnPlayingSet(Animator::pause);
         }
     }
 
@@ -676,6 +682,7 @@
             if (mPauseTime >= 0) {
                 addAnimationCallback(0);
             }
+            callOnPlayingSet(Animator::resume);
         }
     }
 
@@ -751,26 +758,14 @@
 
     private void notifyStartListeners(boolean inReverse) {
         if (mListeners != null && !mStartListenersCalled) {
-            ArrayList<AnimatorListener> tmpListeners =
-                    (ArrayList<AnimatorListener>) mListeners.clone();
-            int numListeners = tmpListeners.size();
-            for (int i = 0; i < numListeners; ++i) {
-                AnimatorListener listener = tmpListeners.get(i);
-                listener.onAnimationStart(this, inReverse);
-            }
+            notifyListeners(AnimatorCaller.ON_START, inReverse);
         }
         mStartListenersCalled = true;
     }
 
     private void notifyEndListeners(boolean inReverse) {
         if (mListeners != null && mStartListenersCalled) {
-            ArrayList<AnimatorListener> tmpListeners =
-                    (ArrayList<AnimatorListener>) mListeners.clone();
-            int numListeners = tmpListeners.size();
-            for (int i = 0; i < numListeners; ++i) {
-                AnimatorListener listener = tmpListeners.get(i);
-                listener.onAnimationEnd(this, inReverse);
-            }
+            notifyListeners(AnimatorCaller.ON_END, inReverse);
         }
         mStartListenersCalled = false;
     }
@@ -1503,6 +1498,7 @@
         anim.mNodeMap = new ArrayMap<Animator, Node>();
         anim.mNodes = new ArrayList<Node>(nodeCount);
         anim.mEvents = new ArrayList<AnimationEvent>();
+        anim.mStartListenersCalled = false;
         anim.mAnimationEndListener = new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index 7009725..46d9847 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -1110,24 +1110,14 @@
 
     private void notifyStartListeners(boolean isReversing) {
         if (mListeners != null && !mStartListenersCalled) {
-            ArrayList<AnimatorListener> tmpListeners =
-                    (ArrayList<AnimatorListener>) mListeners.clone();
-            int numListeners = tmpListeners.size();
-            for (int i = 0; i < numListeners; ++i) {
-                tmpListeners.get(i).onAnimationStart(this, isReversing);
-            }
+            notifyListeners(AnimatorCaller.ON_START, isReversing);
         }
         mStartListenersCalled = true;
     }
 
     private void notifyEndListeners(boolean isReversing) {
         if (mListeners != null && mStartListenersCalled) {
-            ArrayList<AnimatorListener> tmpListeners =
-                    (ArrayList<AnimatorListener>) mListeners.clone();
-            int numListeners = tmpListeners.size();
-            for (int i = 0; i < numListeners; ++i) {
-                tmpListeners.get(i).onAnimationEnd(this, isReversing);
-            }
+            notifyListeners(AnimatorCaller.ON_END, isReversing);
         }
         mStartListenersCalled = false;
     }
@@ -1224,15 +1214,7 @@
                 // If it's not yet running, then start listeners weren't called. Call them now.
                 notifyStartListeners(mReversing);
             }
-            int listenersSize = mListeners.size();
-            if (listenersSize > 0) {
-                ArrayList<AnimatorListener> tmpListeners =
-                        (ArrayList<AnimatorListener>) mListeners.clone();
-                for (int i = 0; i < listenersSize; i++) {
-                    AnimatorListener listener = tmpListeners.get(i);
-                    listener.onAnimationCancel(this);
-                }
-            }
+            notifyListeners(AnimatorCaller.ON_CANCEL, false);
         }
         endAnimation();
 
@@ -1435,12 +1417,7 @@
                 done = true;
             } else if (newIteration && !lastIterationFinished) {
                 // Time to repeat
-                if (mListeners != null) {
-                    int numListeners = mListeners.size();
-                    for (int i = 0; i < numListeners; ++i) {
-                        mListeners.get(i).onAnimationRepeat(this);
-                    }
-                }
+                notifyListeners(AnimatorCaller.ON_REPEAT, false);
             } else if (lastIterationFinished) {
                 done = true;
             }
@@ -1494,12 +1471,7 @@
             lastIteration = Math.min(lastIteration, mRepeatCount);
 
             if (iteration != lastIteration) {
-                if (mListeners != null) {
-                    int numListeners = mListeners.size();
-                    for (int i = 0; i < numListeners; ++i) {
-                        mListeners.get(i).onAnimationRepeat(this);
-                    }
-                }
+                notifyListeners(AnimatorCaller.ON_REPEAT, false);
             }
         }
 
@@ -1697,12 +1669,7 @@
         for (int i = 0; i < numValues; ++i) {
             mValues[i].calculateValue(fraction);
         }
-        if (mUpdateListeners != null) {
-            int numListeners = mUpdateListeners.size();
-            for (int i = 0; i < numListeners; ++i) {
-                mUpdateListeners.get(i).onAnimationUpdate(this);
-            }
-        }
+        callOnList(mUpdateListeners, AnimatorCaller.ON_UPDATE, this, false);
     }
 
     @Override
diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java
index f35bdfb..fe40a4c 100644
--- a/core/java/android/app/BroadcastOptions.java
+++ b/core/java/android/app/BroadcastOptions.java
@@ -262,7 +262,12 @@
      * Creates a basic {@link BroadcastOptions} with no options initially set.
      *
      * @return an instance of {@code BroadcastOptions} against which options can be set
+     *
+     * @deprecated Use {@link BroadcastOptions#BroadcastOptions()} instead.
+     * @hide
      */
+    @Deprecated
+    @SystemApi
     public static @NonNull BroadcastOptions makeBasic() {
         BroadcastOptions opts = new BroadcastOptions();
         return opts;
@@ -280,7 +285,10 @@
         return opts;
     }
 
-    private BroadcastOptions() {
+    /**
+     * Creates a new {@code BroadcastOptions} with no options initially set.
+     */
+    public BroadcastOptions() {
         super();
         resetTemporaryAppAllowlist();
     }
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index bf69531..dc197bd 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -1401,95 +1401,99 @@
         if (mApplication != null) {
             return mApplication;
         }
-        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "makeApplication");
 
-        synchronized (sApplications) {
-            final Application cached = sApplications.get(mPackageName);
-            if (cached != null) {
-                // Looks like this is always happening for the system server, because
-                // the LoadedApk created in systemMain() -> attach() isn't cached properly?
-                if (!"android".equals(mPackageName)) {
-                    Slog.wtfStack(TAG, "App instance already created for package=" + mPackageName
-                            + " instance=" + cached);
-                }
-                if (!allowDuplicateInstances) {
-                    mApplication = cached;
-                    return cached;
-                }
-                // Some apps intentionally call makeApplication() to create a new Application
-                // instance... Sigh...
-            }
-        }
 
-        Application app = null;
-
-        final String myProcessName = Process.myProcessName();
-        String appClass = mApplicationInfo.getCustomApplicationClassNameForProcess(
-                myProcessName);
-        if (forceDefaultAppClass || (appClass == null)) {
-            appClass = "android.app.Application";
+        if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "makeApplication");
         }
 
         try {
-            final java.lang.ClassLoader cl = getClassLoader();
-            if (!mPackageName.equals("android")) {
-                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
-                        "initializeJavaContextClassLoader");
-                initializeJavaContextClassLoader();
-                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-            }
-
-            // Rewrite the R 'constants' for all library apks.
-            SparseArray<String> packageIdentifiers = getAssets().getAssignedPackageIdentifiers(
-                    false, false);
-            for (int i = 0, n = packageIdentifiers.size(); i < n; i++) {
-                final int id = packageIdentifiers.keyAt(i);
-                if (id == 0x01 || id == 0x7f) {
-                    continue;
-                }
-
-                rewriteRValues(cl, packageIdentifiers.valueAt(i), id);
-            }
-
-            ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
-            // The network security config needs to be aware of multiple
-            // applications in the same process to handle discrepancies
-            NetworkSecurityConfigProvider.handleNewApplication(appContext);
-            app = mActivityThread.mInstrumentation.newApplication(
-                    cl, appClass, appContext);
-            appContext.setOuterContext(app);
-        } catch (Exception e) {
-            if (!mActivityThread.mInstrumentation.onException(app, e)) {
-                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-                throw new RuntimeException(
-                    "Unable to instantiate application " + appClass
-                    + " package " + mPackageName + ": " + e.toString(), e);
-            }
-        }
-        mActivityThread.mAllApplications.add(app);
-        mApplication = app;
-        if (!allowDuplicateInstances) {
             synchronized (sApplications) {
-                sApplications.put(mPackageName, app);
-            }
-        }
-
-        if (instrumentation != null) {
-            try {
-                instrumentation.callApplicationOnCreate(app);
-            } catch (Exception e) {
-                if (!instrumentation.onException(app, e)) {
-                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-                    throw new RuntimeException(
-                        "Unable to create application " + app.getClass().getName()
-                        + ": " + e.toString(), e);
+                final Application cached = sApplications.get(mPackageName);
+                if (cached != null) {
+                    // Looks like this is always happening for the system server, because
+                    // the LoadedApk created in systemMain() -> attach() isn't cached properly?
+                    if (!"android".equals(mPackageName)) {
+                        Slog.wtfStack(TAG, "App instance already created for package="
+                                + mPackageName + " instance=" + cached);
+                    }
+                    if (!allowDuplicateInstances) {
+                        mApplication = cached;
+                        return cached;
+                    }
+                    // Some apps intentionally call makeApplication() to create a new Application
+                    // instance... Sigh...
                 }
             }
+
+            Application app = null;
+
+            final String myProcessName = Process.myProcessName();
+            String appClass = mApplicationInfo.getCustomApplicationClassNameForProcess(
+                    myProcessName);
+            if (forceDefaultAppClass || (appClass == null)) {
+                appClass = "android.app.Application";
+            }
+
+            try {
+                final java.lang.ClassLoader cl = getClassLoader();
+                if (!mPackageName.equals("android")) {
+                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+                            "initializeJavaContextClassLoader");
+                    initializeJavaContextClassLoader();
+                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+                }
+
+                // Rewrite the R 'constants' for all library apks.
+                SparseArray<String> packageIdentifiers = getAssets().getAssignedPackageIdentifiers(
+                        false, false);
+                for (int i = 0, n = packageIdentifiers.size(); i < n; i++) {
+                    final int id = packageIdentifiers.keyAt(i);
+                    if (id == 0x01 || id == 0x7f) {
+                        continue;
+                    }
+
+                    rewriteRValues(cl, packageIdentifiers.valueAt(i), id);
+                }
+
+                ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
+                // The network security config needs to be aware of multiple
+                // applications in the same process to handle discrepancies
+                NetworkSecurityConfigProvider.handleNewApplication(appContext);
+                app = mActivityThread.mInstrumentation.newApplication(
+                        cl, appClass, appContext);
+                appContext.setOuterContext(app);
+            } catch (Exception e) {
+                if (!mActivityThread.mInstrumentation.onException(app, e)) {
+                    throw new RuntimeException(
+                        "Unable to instantiate application " + appClass
+                        + " package " + mPackageName + ": " + e.toString(), e);
+                }
+            }
+            mActivityThread.mAllApplications.add(app);
+            mApplication = app;
+            if (!allowDuplicateInstances) {
+                synchronized (sApplications) {
+                    sApplications.put(mPackageName, app);
+                }
+            }
+
+            if (instrumentation != null) {
+                try {
+                    instrumentation.callApplicationOnCreate(app);
+                } catch (Exception e) {
+                    if (!instrumentation.onException(app, e)) {
+                        throw new RuntimeException(
+                            "Unable to create application " + app.getClass().getName()
+                            + ": " + e.toString(), e);
+                    }
+                }
+            }
+
+            return app;
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
         }
-
-        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-
-        return app;
     }
 
     @UnsupportedAppUsage
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 3a3ad8ce..352e753 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5249,17 +5249,17 @@
             boolean hasSecondLine = showProgress;
             if (p.hasTitle()) {
                 contentView.setViewVisibility(p.mTitleViewId, View.VISIBLE);
-                contentView.setTextViewText(p.mTitleViewId, processTextSpans(p.title));
+                contentView.setTextViewText(p.mTitleViewId, processTextSpans(p.mTitle));
                 setTextViewColorPrimary(contentView, p.mTitleViewId, p);
             } else if (p.mTitleViewId != R.id.title) {
                 // This alternate title view ID is not cleared by resetStandardTemplate
                 contentView.setViewVisibility(p.mTitleViewId, View.GONE);
                 contentView.setTextViewText(p.mTitleViewId, null);
             }
-            if (p.text != null && p.text.length() != 0
+            if (p.mText != null && p.mText.length() != 0
                     && (!showProgress || p.mAllowTextWithProgress)) {
                 contentView.setViewVisibility(p.mTextViewId, View.VISIBLE);
-                contentView.setTextViewText(p.mTextViewId, processTextSpans(p.text));
+                contentView.setTextViewText(p.mTextViewId, processTextSpans(p.mText));
                 setTextViewColorSecondary(contentView, p.mTextViewId, p);
                 hasSecondLine = true;
             } else if (p.mTextViewId != R.id.text) {
@@ -5533,20 +5533,20 @@
             if (p.mHideSubText) {
                 return false;
             }
-            CharSequence summaryText = p.summaryText;
-            if (summaryText == null && mStyle != null && mStyle.mSummaryTextSet
+            CharSequence headerText = p.mSubText;
+            if (headerText == null && mStyle != null && mStyle.mSummaryTextSet
                     && mStyle.hasSummaryInHeader()) {
-                summaryText = mStyle.mSummaryText;
+                headerText = mStyle.mSummaryText;
             }
-            if (summaryText == null
+            if (headerText == null
                     && mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
                     && mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null) {
-                summaryText = mN.extras.getCharSequence(EXTRA_INFO_TEXT);
+                headerText = mN.extras.getCharSequence(EXTRA_INFO_TEXT);
             }
-            if (!TextUtils.isEmpty(summaryText)) {
+            if (!TextUtils.isEmpty(headerText)) {
                 // TODO: Remove the span entirely to only have the string with propper formating.
                 contentView.setTextViewText(R.id.header_text, processTextSpans(
-                        processLegacyText(summaryText)));
+                        processLegacyText(headerText)));
                 setTextViewColorSecondary(contentView, R.id.header_text, p);
                 contentView.setViewVisibility(R.id.header_text, View.VISIBLE);
                 if (hasTextToLeft) {
@@ -5566,9 +5566,9 @@
             if (p.mHideSubText) {
                 return false;
             }
-            if (!TextUtils.isEmpty(p.headerTextSecondary)) {
+            if (!TextUtils.isEmpty(p.mHeaderTextSecondary)) {
                 contentView.setTextViewText(R.id.header_text_secondary, processTextSpans(
-                        processLegacyText(p.headerTextSecondary)));
+                        processLegacyText(p.mHeaderTextSecondary)));
                 setTextViewColorSecondary(contentView, R.id.header_text_secondary, p);
                 contentView.setViewVisibility(R.id.header_text_secondary, View.VISIBLE);
                 if (hasTextToLeft) {
@@ -6175,7 +6175,7 @@
                     .viewType(StandardTemplateParams.VIEW_TYPE_MINIMIZED)
                     .highlightExpander(false)
                     .fillTextsFrom(this);
-            if (!useRegularSubtext || TextUtils.isEmpty(p.summaryText)) {
+            if (!useRegularSubtext || TextUtils.isEmpty(p.mSubText)) {
                 p.summaryText(createSummaryText());
             }
             RemoteViews header = makeNotificationHeader(p);
@@ -7190,7 +7190,7 @@
             checkBuilder();
 
             if (mBigContentTitle != null) {
-                p.title = mBigContentTitle;
+                p.mTitle = mBigContentTitle;
             }
 
             return mBuilder.applyStandardTemplateWithActions(layoutId, p, result);
@@ -12453,10 +12453,10 @@
         boolean mAllowTextWithProgress;
         int mTitleViewId;
         int mTextViewId;
-        CharSequence title;
-        CharSequence text;
-        CharSequence headerTextSecondary;
-        CharSequence summaryText;
+        @Nullable CharSequence mTitle;
+        @Nullable CharSequence mText;
+        @Nullable CharSequence mHeaderTextSecondary;
+        @Nullable CharSequence mSubText;
         int maxRemoteInputHistory = Style.MAX_REMOTE_INPUT_HISTORY_LINES;
         boolean allowColorization  = true;
         boolean mHighlightExpander = false;
@@ -12478,10 +12478,10 @@
             mAllowTextWithProgress = false;
             mTitleViewId = R.id.title;
             mTextViewId = R.id.text;
-            title = null;
-            text = null;
-            summaryText = null;
-            headerTextSecondary = null;
+            mTitle = null;
+            mText = null;
+            mSubText = null;
+            mHeaderTextSecondary = null;
             maxRemoteInputHistory = Style.MAX_REMOTE_INPUT_HISTORY_LINES;
             allowColorization = true;
             mHighlightExpander = false;
@@ -12489,7 +12489,7 @@
         }
 
         final boolean hasTitle() {
-            return !TextUtils.isEmpty(title) && !mHideTitle;
+            return !TextUtils.isEmpty(mTitle) && !mHideTitle;
         }
 
         final StandardTemplateParams viewType(int viewType) {
@@ -12562,23 +12562,23 @@
             return this;
         }
 
-        final StandardTemplateParams title(CharSequence title) {
-            this.title = title;
+        final StandardTemplateParams title(@Nullable CharSequence title) {
+            this.mTitle = title;
             return this;
         }
 
-        final StandardTemplateParams text(CharSequence text) {
-            this.text = text;
+        final StandardTemplateParams text(@Nullable CharSequence text) {
+            this.mText = text;
             return this;
         }
 
-        final StandardTemplateParams summaryText(CharSequence text) {
-            this.summaryText = text;
+        final StandardTemplateParams summaryText(@Nullable CharSequence text) {
+            this.mSubText = text;
             return this;
         }
 
-        final StandardTemplateParams headerTextSecondary(CharSequence text) {
-            this.headerTextSecondary = text;
+        final StandardTemplateParams headerTextSecondary(@Nullable CharSequence text) {
+            this.mHeaderTextSecondary = text;
             return this;
         }
 
@@ -12605,9 +12605,9 @@
 
         final StandardTemplateParams fillTextsFrom(Builder b) {
             Bundle extras = b.mN.extras;
-            this.title = b.processLegacyText(extras.getCharSequence(EXTRA_TITLE));
-            this.text = b.processLegacyText(extras.getCharSequence(EXTRA_TEXT));
-            this.summaryText = extras.getCharSequence(EXTRA_SUB_TEXT);
+            this.mTitle = b.processLegacyText(extras.getCharSequence(EXTRA_TITLE));
+            this.mText = b.processLegacyText(extras.getCharSequence(EXTRA_TEXT));
+            this.mSubText = extras.getCharSequence(EXTRA_SUB_TEXT);
             return this;
         }
 
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index e8f0a89..f55f355 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -19,7 +19,7 @@
 import static android.Manifest.permission.INTERACT_ACROSS_USERS;
 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
 import static android.os.Process.myUserHandle;
-import static android.os.Trace.TRACE_TAG_DATABASE;
+import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 
 import static com.android.internal.util.FrameworkStatsLog.GET_TYPE_ACCESSED_WITHOUT_PERMISSION;
 import static com.android.internal.util.FrameworkStatsLog.GET_TYPE_ACCESSED_WITHOUT_PERMISSION__LOCATION__PROVIDER_CHECK_URI_PERMISSION;
@@ -284,7 +284,7 @@
                 // Return an empty cursor for all columns.
                 return new MatrixCursor(cursor.getColumnNames(), 0);
             }
-            traceBegin(TRACE_TAG_DATABASE, "query: ", uri.getAuthority());
+            traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "query: ", uri.getAuthority());
             final AttributionSource original = setCallingAttributionSource(
                     attributionSource);
             try {
@@ -295,7 +295,7 @@
                 throw e.rethrowAsRuntimeException();
             } finally {
                 setCallingAttributionSource(original);
-                Trace.traceEnd(TRACE_TAG_DATABASE);
+                Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
             }
         }
 
@@ -304,7 +304,7 @@
             // getCallingPackage() isn't available in getType(), as the javadoc states.
             uri = validateIncomingUri(uri);
             uri = maybeGetUriWithoutUserId(uri);
-            traceBegin(TRACE_TAG_DATABASE, "getType: ", uri.getAuthority());
+            traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "getType: ", uri.getAuthority());
             try {
                 final String type = mInterface.getType(uri);
                 if (type != null) {
@@ -314,7 +314,7 @@
             } catch (RemoteException e) {
                 throw e.rethrowAsRuntimeException();
             } finally {
-                Trace.traceEnd(TRACE_TAG_DATABASE);
+                Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
             }
         }
 
@@ -395,7 +395,7 @@
                     setCallingAttributionSource(original);
                 }
             }
-            traceBegin(TRACE_TAG_DATABASE, "insert: ", uri.getAuthority());
+            traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "insert: ", uri.getAuthority());
             final AttributionSource original = setCallingAttributionSource(
                     attributionSource);
             try {
@@ -404,7 +404,7 @@
                 throw e.rethrowAsRuntimeException();
             } finally {
                 setCallingAttributionSource(original);
-                Trace.traceEnd(TRACE_TAG_DATABASE);
+                Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
             }
         }
 
@@ -417,7 +417,7 @@
                     != PermissionChecker.PERMISSION_GRANTED) {
                 return 0;
             }
-            traceBegin(TRACE_TAG_DATABASE, "bulkInsert: ", uri.getAuthority());
+            traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "bulkInsert: ", uri.getAuthority());
             final AttributionSource original = setCallingAttributionSource(
                     attributionSource);
             try {
@@ -426,7 +426,7 @@
                 throw e.rethrowAsRuntimeException();
             } finally {
                 setCallingAttributionSource(original);
-                Trace.traceEnd(TRACE_TAG_DATABASE);
+                Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
             }
         }
 
@@ -463,7 +463,7 @@
                     }
                 }
             }
-            traceBegin(TRACE_TAG_DATABASE, "applyBatch: ", authority);
+            traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "applyBatch: ", authority);
             final AttributionSource original = setCallingAttributionSource(
                     attributionSource);
             try {
@@ -482,7 +482,7 @@
                 throw e.rethrowAsRuntimeException();
             } finally {
                 setCallingAttributionSource(original);
-                Trace.traceEnd(TRACE_TAG_DATABASE);
+                Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
             }
         }
 
@@ -495,7 +495,7 @@
                     != PermissionChecker.PERMISSION_GRANTED) {
                 return 0;
             }
-            traceBegin(TRACE_TAG_DATABASE, "delete: ", uri.getAuthority());
+            traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "delete: ", uri.getAuthority());
             final AttributionSource original = setCallingAttributionSource(
                     attributionSource);
             try {
@@ -504,7 +504,7 @@
                 throw e.rethrowAsRuntimeException();
             } finally {
                 setCallingAttributionSource(original);
-                Trace.traceEnd(TRACE_TAG_DATABASE);
+                Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
             }
         }
 
@@ -517,7 +517,7 @@
                     != PermissionChecker.PERMISSION_GRANTED) {
                 return 0;
             }
-            traceBegin(TRACE_TAG_DATABASE, "update: ", uri.getAuthority());
+            traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "update: ", uri.getAuthority());
             final AttributionSource original = setCallingAttributionSource(
                     attributionSource);
             try {
@@ -526,7 +526,7 @@
                 throw e.rethrowAsRuntimeException();
             } finally {
                 setCallingAttributionSource(original);
-                Trace.traceEnd(TRACE_TAG_DATABASE);
+                Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
             }
         }
 
@@ -537,7 +537,7 @@
             uri = validateIncomingUri(uri);
             uri = maybeGetUriWithoutUserId(uri);
             enforceFilePermission(attributionSource, uri, mode);
-            traceBegin(TRACE_TAG_DATABASE, "openFile: ", uri.getAuthority());
+            traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "openFile: ", uri.getAuthority());
             final AttributionSource original = setCallingAttributionSource(
                     attributionSource);
             try {
@@ -547,7 +547,7 @@
                 throw e.rethrowAsRuntimeException();
             } finally {
                 setCallingAttributionSource(original);
-                Trace.traceEnd(TRACE_TAG_DATABASE);
+                Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
             }
         }
 
@@ -558,7 +558,7 @@
             uri = validateIncomingUri(uri);
             uri = maybeGetUriWithoutUserId(uri);
             enforceFilePermission(attributionSource, uri, mode);
-            traceBegin(TRACE_TAG_DATABASE, "openAssetFile: ", uri.getAuthority());
+            traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "openAssetFile: ", uri.getAuthority());
             final AttributionSource original = setCallingAttributionSource(
                     attributionSource);
             try {
@@ -568,7 +568,7 @@
                 throw e.rethrowAsRuntimeException();
             } finally {
                 setCallingAttributionSource(original);
-                Trace.traceEnd(TRACE_TAG_DATABASE);
+                Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
             }
         }
 
@@ -577,7 +577,7 @@
                 String method, @Nullable String arg, @Nullable Bundle extras) {
             validateIncomingAuthority(authority);
             Bundle.setDefusable(extras, true);
-            traceBegin(TRACE_TAG_DATABASE, "call: ", authority);
+            traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "call: ", authority);
             final AttributionSource original = setCallingAttributionSource(
                     attributionSource);
             try {
@@ -586,7 +586,7 @@
                 throw e.rethrowAsRuntimeException();
             } finally {
                 setCallingAttributionSource(original);
-                Trace.traceEnd(TRACE_TAG_DATABASE);
+                Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
             }
         }
 
@@ -595,13 +595,13 @@
             // getCallingPackage() isn't available in getType(), as the javadoc states.
             uri = validateIncomingUri(uri);
             uri = maybeGetUriWithoutUserId(uri);
-            traceBegin(TRACE_TAG_DATABASE, "getStreamTypes: ", uri.getAuthority());
+            traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "getStreamTypes: ", uri.getAuthority());
             try {
                 return mInterface.getStreamTypes(uri, mimeTypeFilter);
             } catch (RemoteException e) {
                 throw e.rethrowAsRuntimeException();
             } finally {
-                Trace.traceEnd(TRACE_TAG_DATABASE);
+                Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
             }
         }
 
@@ -613,7 +613,7 @@
             uri = validateIncomingUri(uri);
             uri = maybeGetUriWithoutUserId(uri);
             enforceFilePermission(attributionSource, uri, "r");
-            traceBegin(TRACE_TAG_DATABASE, "openTypedAssetFile: ", uri.getAuthority());
+            traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "openTypedAssetFile: ", uri.getAuthority());
             final AttributionSource original = setCallingAttributionSource(
                     attributionSource);
             try {
@@ -623,7 +623,7 @@
                 throw e.rethrowAsRuntimeException();
             } finally {
                 setCallingAttributionSource(original);
-                Trace.traceEnd(TRACE_TAG_DATABASE);
+                Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
             }
         }
 
@@ -641,7 +641,7 @@
                     != PermissionChecker.PERMISSION_GRANTED) {
                 return null;
             }
-            traceBegin(TRACE_TAG_DATABASE, "canonicalize: ", uri.getAuthority());
+            traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "canonicalize: ", uri.getAuthority());
             final AttributionSource original = setCallingAttributionSource(
                     attributionSource);
             try {
@@ -650,7 +650,7 @@
                 throw e.rethrowAsRuntimeException();
             } finally {
                 setCallingAttributionSource(original);
-                Trace.traceEnd(TRACE_TAG_DATABASE);
+                Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
             }
         }
 
@@ -677,7 +677,7 @@
                     != PermissionChecker.PERMISSION_GRANTED) {
                 return null;
             }
-            traceBegin(TRACE_TAG_DATABASE, "uncanonicalize: ", uri.getAuthority());
+            traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "uncanonicalize: ", uri.getAuthority());
             final AttributionSource original = setCallingAttributionSource(
                     attributionSource);
             try {
@@ -686,7 +686,7 @@
                 throw e.rethrowAsRuntimeException();
             } finally {
                 setCallingAttributionSource(original);
-                Trace.traceEnd(TRACE_TAG_DATABASE);
+                Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
             }
         }
 
@@ -713,7 +713,7 @@
                     != PermissionChecker.PERMISSION_GRANTED) {
                 return false;
             }
-            traceBegin(TRACE_TAG_DATABASE, "refresh: ", uri.getAuthority());
+            traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "refresh: ", uri.getAuthority());
             final AttributionSource original = setCallingAttributionSource(
                     attributionSource);
             try {
@@ -721,7 +721,7 @@
                         CancellationSignal.fromTransport(cancellationSignal));
             } finally {
                 setCallingAttributionSource(original);
-                Trace.traceEnd(TRACE_TAG_DATABASE);
+                Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
             }
         }
 
@@ -730,7 +730,7 @@
                 int uid, int modeFlags) {
             uri = validateIncomingUri(uri);
             uri = maybeGetUriWithoutUserId(uri);
-            traceBegin(TRACE_TAG_DATABASE, "checkUriPermission: ", uri.getAuthority());
+            traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "checkUriPermission: ", uri.getAuthority());
             final AttributionSource original = setCallingAttributionSource(
                     attributionSource);
             try {
@@ -739,7 +739,7 @@
                 throw e.rethrowAsRuntimeException();
             } finally {
                 setCallingAttributionSource(original);
-                Trace.traceEnd(TRACE_TAG_DATABASE);
+                Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
             }
         }
 
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index 6386f75..81fc029 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -60,10 +60,17 @@
      */
 
     /**
-     * Primary user. Only one user can have this flag set. It identifies the first human user
-     * on a device. This flag is not supported in headless system user mode.
+     * Primary user. In practice, this is just synonymous with {@link #FLAG_SYSTEM}.
+     *
+     * <p>On many devices, this will also be the first human user.
+     * However, in {@link UserManager#isHeadlessSystemUserMode() headless system user mode}, this
+     * should be regarded as unsupported since the system user may not be a human.
+     *
+     * @deprecated For checking for user 0, use {@link #FLAG_SYSTEM}.
+     *             For checking for the designated "main human user", use {@link #FLAG_MAIN}.
      */
     @UnsupportedAppUsage
+    @Deprecated
     public static final int FLAG_PRIMARY = 0x00000001;
 
     /**
@@ -335,7 +342,12 @@
         }
     }
 
+    /**
+     * @deprecated For checking for user 0, compare {@link #id} to {@link UserHandle#USER_SYSTEM}.
+     *             For checking for the designated "main human user", use {@link #isMain()}.
+     */
     @UnsupportedAppUsage
+    @Deprecated
     public boolean isPrimary() {
         return (flags & FLAG_PRIMARY) == FLAG_PRIMARY;
     }
diff --git a/core/java/android/credentials/CredentialDescription.java b/core/java/android/credentials/CredentialDescription.java
index f9db5e8..e74de9c 100644
--- a/core/java/android/credentials/CredentialDescription.java
+++ b/core/java/android/credentials/CredentialDescription.java
@@ -151,16 +151,4 @@
     public List<CredentialEntry> getCredentialEntries() {
         return mCredentialEntries;
     }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(mType, mFlattenedRequestString);
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        return Objects.equals(mType, ((CredentialDescription) obj).getType())
-                && Objects.equals(mFlattenedRequestString, ((CredentialDescription) obj)
-                .getFlattenedRequestString());
-    }
 }
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 2ea9ea0..a33cd97 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -74,6 +74,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -99,13 +100,12 @@
     private final IInputManager mIm;
 
     /**
-     * InputManager has historically used its own static getter {@link #getInstance()} that doesn't
-     * provide a context. We provide a Context to the InputManager instance through the
-     * {@link android.app.SystemServiceRegistry}. Methods that need a Context must use
-     * {@link #getContext()} to obtain it.
+     * We hold a weak reference to the context to avoid leaking it indefinitely,
+     * since we currently store the input manager instance as a static variable that
+     * will outlive any context.
      */
     @Nullable
-    private Context mLateInitContext;
+    private WeakReference<Context> mWeakContext;
 
     /**
      * Whether a PointerIcon is shown for stylus pointers.
@@ -372,8 +372,8 @@
                     throw new IllegalStateException(e);
                 }
             }
-            if (sInstance.mLateInitContext == null) {
-                sInstance.mLateInitContext = context;
+            if (sInstance.mWeakContext == null || sInstance.mWeakContext.get() == null) {
+                sInstance.mWeakContext = new WeakReference(context);
             }
             return sInstance;
         }
@@ -381,9 +381,14 @@
 
     @NonNull
     private Context getContext() {
-        return Objects.requireNonNull(mLateInitContext,
+        WeakReference<Context> weakContext = Objects.requireNonNull(mWeakContext,
                 "A context is required for InputManager. Get the InputManager instance using "
                         + "Context#getSystemService before calling this method.");
+        // If we get at this point, an app calling this function could potentially expect a
+        // Context that has disappeared due to garbage collection. Holding a weak reference
+        // is a temporary solution that should be resolved before the release of a
+        // production version. This is being tracked in b/267758905
+        return Objects.requireNonNull(weakContext.get(), "missing Context");
     }
 
     /**
diff --git a/core/java/android/hardware/input/VirtualTouchEvent.java b/core/java/android/hardware/input/VirtualTouchEvent.java
index 9b1e6e8..a2bb382 100644
--- a/core/java/android/hardware/input/VirtualTouchEvent.java
+++ b/core/java/android/hardware/input/VirtualTouchEvent.java
@@ -18,6 +18,7 @@
 
 import android.annotation.FloatRange;
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.os.Parcel;
@@ -82,6 +83,10 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface Action {}
 
+    // The maximum number of pointers that can be touching the screen at once. (See MAX_POINTERS
+    // in frameworks/native/include/input/Input.h)
+    private static final int MAX_POINTERS = 16;
+
     private final int mPointerId;
     private final @ToolType int mToolType;
     private final @Action int mAction;
@@ -214,9 +219,17 @@
         /**
          * Sets the pointer id of the event.
          *
+         * <p>A Valid pointer id need to be in the range of 0 to 15.
+         *
          * @return this builder, to allow for chaining of calls
          */
-        public @NonNull Builder setPointerId(int pointerId) {
+        public @NonNull Builder setPointerId(
+                @IntRange(from = 0, to = MAX_POINTERS - 1) int pointerId) {
+            if (pointerId < 0 || pointerId > 15) {
+                throw new IllegalArgumentException(
+                        "The pointer id must be in the range 0 - " + (MAX_POINTERS - 1)
+                                + "inclusive, but was: " + pointerId);
+            }
             mPointerId = pointerId;
             return this;
         }
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 9689be2..168db17 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -1204,6 +1204,11 @@
          * Upside Down Cake.
          */
         public static final int UPSIDE_DOWN_CAKE = CUR_DEVELOPMENT;
+
+        /**
+         * Vanilla Ice Cream.
+         */
+        public static final int VANILLA_ICE_CREAM = CUR_DEVELOPMENT;
     }
 
     /** The type of build, like "user" or "eng". */
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index 80ae8a8..6f4cdce 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -104,7 +104,7 @@
         int protocol;
         int portMatcher;
         int portNumber;
-        @nullable String bindAddress;
+        @nullable byte[] localAddress;
     }
 
     @UnsupportedAppUsage
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 1a634f5..1224941 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -32,7 +32,6 @@
 import android.app.PropertyInvalidatedCache;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
-import android.net.LinkAddress;
 import android.service.dreams.Sandman;
 import android.sysprop.InitProperties;
 import android.util.ArrayMap;
@@ -45,6 +44,8 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
 import java.time.Duration;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -3244,11 +3245,11 @@
         /**
          * Constant to indicate the {@link LowPowerStandbyPortDescription} refers to a TCP port.
          */
-        public static final int PROTOCOL_TCP = 1;
+        public static final int PROTOCOL_TCP = 6;
         /**
          * Constant to indicate the {@link LowPowerStandbyPortDescription} refers to a UDP port.
          */
-        public static final int PROTOCOL_UDP = 2;
+        public static final int PROTOCOL_UDP = 17;
 
         /** @hide */
         @IntDef(prefix = { "MATCH_PORT_" }, value = {
@@ -3277,7 +3278,7 @@
         private final int mPortMatcher;
         private final int mPortNumber;
         @Nullable
-        private final LinkAddress mBindAddress;
+        private final InetAddress mLocalAddress;
 
         /**
          * Describes a port.
@@ -3296,7 +3297,7 @@
             this.mProtocol = protocol;
             this.mPortMatcher = portMatcher;
             this.mPortNumber = portNumber;
-            this.mBindAddress = null;
+            this.mLocalAddress = null;
         }
 
         /**
@@ -3308,16 +3309,16 @@
          *                    ({@link #MATCH_PORT_REMOTE}), or the destination port
          *                    ({@link #MATCH_PORT_LOCAL}).
          * @param portNumber The port number to match.
-         * @param bindAddress The bind address to match.
+         * @param localAddress The local address to match.
          *
          * @see #newLowPowerStandbyPortsLock(List)
          */
         public LowPowerStandbyPortDescription(@Protocol int protocol, @PortMatcher int portMatcher,
-                int portNumber, @Nullable LinkAddress bindAddress) {
+                int portNumber, @Nullable InetAddress localAddress) {
             this.mProtocol = protocol;
             this.mPortMatcher = portMatcher;
             this.mPortNumber = portNumber;
-            this.mBindAddress = bindAddress;
+            this.mLocalAddress = localAddress;
         }
 
         private String protocolToString(int protocol) {
@@ -3383,8 +3384,8 @@
          * @see #getProtocol()
          */
         @Nullable
-        public LinkAddress getBindAddress() {
-            return mBindAddress;
+        public InetAddress getLocalAddress() {
+            return mLocalAddress;
         }
 
         @Override
@@ -3393,7 +3394,7 @@
                     + "mProtocol=" + protocolToString(mProtocol)
                     + ", mPortMatcher=" + portMatcherToString(mPortMatcher)
                     + ", mPortNumber=" + mPortNumber
-                    + ", mBindAddress=" + mBindAddress
+                    + ", mLocalAddress=" + mLocalAddress
                     + '}';
         }
 
@@ -3403,13 +3404,13 @@
             if (!(o instanceof LowPowerStandbyPortDescription)) return false;
             LowPowerStandbyPortDescription that = (LowPowerStandbyPortDescription) o;
             return mProtocol == that.mProtocol && mPortMatcher == that.mPortMatcher
-                    && mPortNumber == that.mPortNumber && Objects.equals(mBindAddress,
-                    that.mBindAddress);
+                    && mPortNumber == that.mPortNumber && Objects.equals(mLocalAddress,
+                    that.mLocalAddress);
         }
 
         @Override
         public int hashCode() {
-            return Objects.hash(mProtocol, mPortMatcher, mPortNumber, mBindAddress);
+            return Objects.hash(mProtocol, mPortMatcher, mPortNumber, mLocalAddress);
         }
 
         /** @hide */
@@ -3424,8 +3425,8 @@
             parcelablePortDescription.protocol = portDescription.mProtocol;
             parcelablePortDescription.portMatcher = portDescription.mPortMatcher;
             parcelablePortDescription.portNumber = portDescription.mPortNumber;
-            if (portDescription.mBindAddress != null) {
-                parcelablePortDescription.bindAddress = portDescription.mBindAddress.toString();
+            if (portDescription.mLocalAddress != null) {
+                parcelablePortDescription.localAddress = portDescription.mLocalAddress.getAddress();
             }
             return parcelablePortDescription;
         }
@@ -3451,15 +3452,19 @@
                 return null;
             }
 
-            LinkAddress bindAddress = null;
-            if (parcelablePortDescription.bindAddress != null) {
-                bindAddress = new LinkAddress(parcelablePortDescription.bindAddress);
+            InetAddress localAddress = null;
+            if (parcelablePortDescription.localAddress != null) {
+                try {
+                    localAddress = InetAddress.getByAddress(parcelablePortDescription.localAddress);
+                } catch (UnknownHostException e) {
+                    Log.w(TAG, "Address has invalid length", e);
+                }
             }
             return new LowPowerStandbyPortDescription(
                     parcelablePortDescription.protocol,
                     parcelablePortDescription.portMatcher,
                     parcelablePortDescription.portNumber,
-                    bindAddress);
+                    localAddress);
         }
 
         /** @hide */
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 9c55ad6..bbf7f81 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -4381,11 +4381,16 @@
     }
 
     /**
-     * Returns information for Primary user.
+     * Returns information for Primary user (which in practice is the same as the System user).
      *
      * @return the Primary user, null if not found.
+     * @deprecated For the system user, call {@link #getUserInfo} on {@link UserHandle#USER_SYSTEM},
+     *             or just use {@link UserHandle#SYSTEM} or {@link UserHandle#USER_SYSTEM}.
+     *             For the designated MainUser, use {@link #getMainUser()}.
+     *
      * @hide
      */
+    @Deprecated
     @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
     public @Nullable UserInfo getPrimaryUser() {
         try {
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 5778518..cabcae3 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -26,6 +26,7 @@
 import android.annotation.Nullable;
 import android.annotation.TestApi;
 import android.app.Activity;
+import android.app.ActivityOptions;
 import android.app.Dialog;
 import android.app.DirectAction;
 import android.app.Instrumentation;
@@ -1527,8 +1528,34 @@
      * <p>By default, the system will create a window for the UI for this session.  If you are using
      * an assistant activity instead, then you can disable the window creation by calling
      * {@link #setUiEnabled} in {@link #onPrepareShow(Bundle, int)}.</p>
+     *
+     * NOTE: if the app would like to override some options to start the Activity,
+     * use {@link #startAssistantActivity(Intent, Bundle)} instead.
      */
     public void startAssistantActivity(Intent intent) {
+        startAssistantActivity(intent, ActivityOptions.makeBasic().toBundle());
+    }
+
+    /**
+     * <p>Ask that a new assistant activity be started.  This will create a new task in the
+     * in activity manager: this means that
+     * {@link Intent#FLAG_ACTIVITY_NEW_TASK Intent.FLAG_ACTIVITY_NEW_TASK}
+     * will be set for you to make it a new task.</p>
+     *
+     * <p>The newly started activity will be displayed on top of other activities in the system
+     * in a new layer that is not affected by multi-window mode.  Tasks started from this activity
+     * will go into the normal activity layer and not this new layer.</p>
+     *
+     * <p>By default, the system will create a window for the UI for this session.  If you are using
+     * an assistant activity instead, then you can disable the window creation by calling
+     * {@link #setUiEnabled} in {@link #onPrepareShow(Bundle, int)}.</p>
+     *
+     * @param intent the intent used to start an assistant activity
+     * @param bundle Additional options for how the Activity should be started. See
+     * {@link ActivityOptions} for how to build the Bundle supplied here.
+     */
+    public void startAssistantActivity(@NonNull Intent intent, @NonNull Bundle bundle) {
+        Objects.requireNonNull(bundle);
         if (mToken == null) {
             throw new IllegalStateException("Can't call before onCreate()");
         }
@@ -1537,7 +1564,7 @@
             intent.prepareToLeaveProcess(mContext);
             int res = mSystemService.startAssistantActivity(mToken, intent,
                     intent.resolveType(mContext.getContentResolver()),
-                    mContext.getAttributionTag());
+                    mContext.getAttributionTag(), bundle);
             Instrumentation.checkStartActivityResult(res, intent);
         } catch (RemoteException e) {
         }
diff --git a/core/java/android/service/wallpaper/IWallpaperService.aidl b/core/java/android/service/wallpaper/IWallpaperService.aidl
index f46c60f..da56b4b 100644
--- a/core/java/android/service/wallpaper/IWallpaperService.aidl
+++ b/core/java/android/service/wallpaper/IWallpaperService.aidl
@@ -26,5 +26,5 @@
     void attach(IWallpaperConnection connection,
             IBinder windowToken, int windowType, boolean isPreview,
             int reqWidth, int reqHeight, in Rect padding, int displayId, int which);
-    void detach();
+    void detach(IBinder windowToken);
 }
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index d21e5df..390fe52 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -68,9 +68,11 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.Trace;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.MergedConfiguration;
+import android.util.Slog;
 import android.view.Display;
 import android.view.DisplayCutout;
 import android.view.Gravity;
@@ -102,7 +104,6 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
@@ -177,8 +178,7 @@
 
     private static final long DIMMING_ANIMATION_DURATION_MS = 300L;
 
-    private final ArrayList<Engine> mActiveEngines
-            = new ArrayList<Engine>();
+    private final ArrayMap<IBinder, IWallpaperEngineWrapper> mActiveEngines = new ArrayMap<>();
 
     static final class WallpaperCommand {
         String action;
@@ -2231,7 +2231,6 @@
         final DisplayManager mDisplayManager;
         final Display mDisplay;
         final WallpaperManager mWallpaperManager;
-        private final AtomicBoolean mDetached = new AtomicBoolean();
 
         Engine mEngine;
         @SetWallpaperFlags int mWhich;
@@ -2346,21 +2345,18 @@
             mEngine.removeLocalColorsAreas(regions);
         }
 
-        public void destroy() {
-            Message msg = mCaller.obtainMessage(DO_DETACH);
-            mCaller.sendMessage(msg);
-        }
-
-        public void detach() {
-            mDetached.set(true);
-        }
-
         public void applyDimming(float dimAmount) throws RemoteException {
             Message msg = mCaller.obtainMessageI(MSG_UPDATE_DIMMING,
                     Float.floatToIntBits(dimAmount));
             mCaller.sendMessage(msg);
         }
 
+        public void destroy() {
+            Message msg = mCaller.obtainMessage(DO_DETACH);
+            mCaller.getHandler().removeCallbacksAndMessages(null);
+            mCaller.sendMessage(msg);
+        }
+
         public void resizePreview(Rect position) {
             Message msg = mCaller.obtainMessageO(MSG_RESIZE_PREVIEW, position);
             mCaller.sendMessage(msg);
@@ -2383,25 +2379,27 @@
                 engine.detach();
                 Log.w(TAG, "Wallpaper host disappeared", e);
                 return;
+            } catch (IllegalStateException e) {
+                Log.w(TAG, "Connector instance already destroyed, "
+                                + "can't attach engine to non existing connector", e);
+                return;
             } finally {
                 Trace.endSection();
             }
-            mActiveEngines.add(engine);
             Trace.beginSection("WPMS.engine.attach");
             engine.attach(this);
             Trace.endSection();
         }
 
         private void doDetachEngine() {
-            mActiveEngines.remove(mEngine);
-            mEngine.detach();
             // Some wallpapers will not trigger the rendering threads of the remaining engines even
             // if they are visible, so we need to toggle the state to get their attention.
-            if (!mDetached.get()) {
-                for (Engine eng : mActiveEngines) {
-                    if (eng.mVisible) {
-                        eng.doVisibilityChanged(false);
-                        eng.doVisibilityChanged(true);
+            if (!mEngine.mDestroyed) {
+                mEngine.detach();
+                for (IWallpaperEngineWrapper engineWrapper : mActiveEngines.values()) {
+                    if (engineWrapper.mEngine != null && engineWrapper.mEngine.mVisible) {
+                        engineWrapper.mEngine.doVisibilityChanged(false);
+                        engineWrapper.mEngine.doVisibilityChanged(true);
                     }
                 }
             }
@@ -2409,12 +2407,6 @@
 
         @Override
         public void executeMessage(Message message) {
-            if (mDetached.get()) {
-                if (mActiveEngines.contains(mEngine)) {
-                    doDetachEngine();
-                }
-                return;
-            }
             switch (message.what) {
                 case DO_ATTACH: {
                     Trace.beginSection("WPMS.DO_ATTACH");
@@ -2525,7 +2517,6 @@
      */
     class IWallpaperServiceWrapper extends IWallpaperService.Stub {
         private final WallpaperService mTarget;
-        private IWallpaperEngineWrapper mEngineWrapper;
 
         public IWallpaperServiceWrapper(WallpaperService context) {
             mTarget = context;
@@ -2536,14 +2527,27 @@
                 int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding,
                 int displayId, @SetWallpaperFlags int which) {
             Trace.beginSection("WPMS.ServiceWrapper.attach");
-            mEngineWrapper = new IWallpaperEngineWrapper(mTarget, conn, windowToken,
-                    windowType, isPreview, reqWidth, reqHeight, padding, displayId, which);
+            IWallpaperEngineWrapper engineWrapper =
+                    new IWallpaperEngineWrapper(mTarget, conn, windowToken, windowType,
+                            isPreview, reqWidth, reqHeight, padding, displayId, which);
+            mActiveEngines.put(windowToken, engineWrapper);
+            if (DEBUG) {
+                Slog.v(TAG, "IWallpaperServiceWrapper Attaching window token " + windowToken);
+            }
             Trace.endSection();
         }
 
         @Override
-        public void detach() {
-            mEngineWrapper.detach();
+        public void detach(IBinder windowToken) {
+            IWallpaperEngineWrapper engineWrapper = mActiveEngines.remove(windowToken);
+            if (engineWrapper == null) {
+                Log.w(TAG, "Engine for window token " + windowToken + " already detached");
+                return;
+            }
+            if (DEBUG) {
+                Slog.v(TAG, "IWallpaperServiceWrapper Detaching window token " + windowToken);
+            }
+            engineWrapper.destroy();
         }
     }
 
@@ -2558,8 +2562,8 @@
     public void onDestroy() {
         Trace.beginSection("WPMS.onDestroy");
         super.onDestroy();
-        for (int i=0; i<mActiveEngines.size(); i++) {
-            mActiveEngines.get(i).detach();
+        for (IWallpaperEngineWrapper engineWrapper : mActiveEngines.values()) {
+            engineWrapper.destroy();
         }
         mActiveEngines.clear();
         Trace.endSection();
@@ -2586,8 +2590,12 @@
     @Override
     protected void dump(FileDescriptor fd, PrintWriter out, String[] args) {
         out.print("State of wallpaper "); out.print(this); out.println(":");
-        for (int i=0; i<mActiveEngines.size(); i++) {
-            Engine engine = mActiveEngines.get(i);
+        for (IWallpaperEngineWrapper engineWrapper : mActiveEngines.values()) {
+            Engine engine = engineWrapper.mEngine;
+            if (engine == null) {
+                Slog.w(TAG, "Engine for wrapper " + engineWrapper + " not attached");
+                continue;
+            }
             out.print("  Engine "); out.print(engine); out.println(":");
             engine.dump("    ", fd, out, args);
         }
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 725eb51..e7cefd6 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -108,6 +108,7 @@
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.SystemProperties;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.proto.ProtoOutputStream;
@@ -1105,6 +1106,26 @@
     public static final String PARCEL_KEY_SHORTCUTS_ARRAY = "shortcuts_array";
 
     /**
+     * Whether the device supports the WindowManager Extensions.
+     * OEMs can enable this by having their device config to inherit window_extensions.mk, such as:
+     * <pre>
+     * $(call inherit-product, $(SRC_TARGET_DIR)/product/window_extensions.mk)
+     * </pre>
+     * @hide
+     */
+    boolean WINDOW_EXTENSIONS_ENABLED =
+            SystemProperties.getBoolean("persist.wm.extensions.enabled", false);
+
+    /**
+     * @see #WINDOW_EXTENSIONS_ENABLED
+     * @hide
+     */
+    @TestApi
+    static boolean hasWindowExtensionsEnabled() {
+        return WINDOW_EXTENSIONS_ENABLED;
+    }
+
+    /**
      * Application-level
      * {@link android.content.pm.PackageManager.Property PackageManager.Property}
      * tag that specifies whether OEMs are permitted to provide activity
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index 5eb9786..6b40d98 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -51,7 +51,7 @@
     int startVoiceActivity(IBinder token, in Intent intent, String resolvedType,
             String attributionTag);
     int startAssistantActivity(IBinder token, in Intent intent, String resolvedType,
-            String attributionTag);
+            String attributionTag, in Bundle bundle);
     void setKeepAwake(IBinder token, boolean keepAwake);
     void closeSystemDialogs(IBinder token);
     void finish(IBinder token);
diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java
index 47b83be..ae192a4 100644
--- a/core/java/com/android/internal/app/IntentForwarderActivity.java
+++ b/core/java/com/android/internal/app/IntentForwarderActivity.java
@@ -45,8 +45,13 @@
 import android.os.UserManager;
 import android.provider.Settings;
 import android.util.Slog;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
 import android.widget.Toast;
 
+import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -151,17 +156,60 @@
                     if (isResolverActivityResolveInfo(targetResolveInfo)) {
                         launchResolverActivityWithCorrectTab(intentReceived, className, newIntent,
                                 callingUserId, targetUserId);
-                        return targetResolveInfo;
+                    // When switching to the personal profile, automatically start the activity
+                    } else if (className.equals(FORWARD_INTENT_TO_PARENT)) {
+                        startActivityAsCaller(newIntent, targetUserId);
                     }
-                    startActivityAsCaller(newIntent, targetUserId);
                     return targetResolveInfo;
                 }, mExecutorService)
                 .thenAcceptAsync(result -> {
-                    maybeShowDisclosure(intentReceived, result, userMessage);
-                    finish();
+                    // When switching to the personal profile, inform user after starting activity
+                    if (className.equals(FORWARD_INTENT_TO_PARENT)) {
+                        maybeShowDisclosure(intentReceived, result, userMessage);
+                        finish();
+                    // When switching to the work profile, ask the user for consent before launching
+                    } else if (className.equals(FORWARD_INTENT_TO_MANAGED_PROFILE)) {
+                        maybeShowUserConsentMiniResolver(result, newIntent, targetUserId);
+                    }
                 }, getApplicationContext().getMainExecutor());
     }
 
+    private void maybeShowUserConsentMiniResolver(
+            ResolveInfo target, Intent launchIntent, int targetUserId) {
+        if (target == null || isIntentForwarderResolveInfo(target) || !isDeviceProvisioned()) {
+            finish();
+            return;
+        }
+
+        int layoutId = R.layout.miniresolver;
+        setContentView(layoutId);
+
+        findViewById(R.id.title_container).setElevation(0);
+
+        ImageView icon = findViewById(R.id.icon);
+        PackageManager packageManagerForTargetUser =
+                createContextAsUser(UserHandle.of(targetUserId), /* flags= */ 0)
+                        .getPackageManager();
+        icon.setImageDrawable(target.loadIcon(packageManagerForTargetUser));
+
+        View buttonContainer = findViewById(R.id.button_bar_container);
+        buttonContainer.setPadding(0, 0, 0, buttonContainer.getPaddingBottom());
+
+        ((TextView) findViewById(R.id.open_cross_profile)).setText(
+                getResources().getString(
+                        R.string.miniresolver_open_in_work,
+                        target.loadLabel(packageManagerForTargetUser)));
+
+        // The mini-resolver's negative button is reused in this flow to cancel the intent
+        ((Button) findViewById(R.id.use_same_profile_browser)).setText(R.string.cancel);
+        findViewById(R.id.use_same_profile_browser).setOnClickListener(v -> finish());
+
+        findViewById(R.id.button_open).setOnClickListener(v -> {
+            startActivityAsCaller(launchIntent, targetUserId);
+            finish();
+        });
+    }
+
     private String getForwardToPersonalMessage() {
         return getSystemService(DevicePolicyManager.class).getResources().getString(
                 FORWARD_INTENT_TO_PERSONAL,
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index e9771a9..22dbb26 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -7688,8 +7688,11 @@
         </activity>
         <activity android:name="com.android.internal.app.IntentForwarderActivity"
                 android:finishOnCloseSystemDialogs="true"
-                android:theme="@style/Theme.Translucent.NoTitleBar"
+                android:theme="@style/Theme.DeviceDefault.Resolver"
                 android:excludeFromRecents="true"
+                android:documentLaunchMode="never"
+                android:relinquishTaskIdentity="true"
+                android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"
                 android:label="@string/user_owner_label"
                 android:exported="true"
                 android:visibleToInstantApps="true"
diff --git a/core/res/res/layout/miniresolver.xml b/core/res/res/layout/miniresolver.xml
index 38a71f0..d07ad89 100644
--- a/core/res/res/layout/miniresolver.xml
+++ b/core/res/res/layout/miniresolver.xml
@@ -14,6 +14,11 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
+
+<!-- Layout used to decide whether to launch a single target in another profile.
+     When this layout is used in ResolverActivity, the user can choose between a verified app in the
+     other profile and the default browser in the current profile.
+     In IntentForwarderActivity, they choose whether to launch in the other profile or cancel. -->
 <com.android.internal.widget.ResolverDrawerLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
@@ -24,6 +29,7 @@
     android:id="@id/contentPanel">
 
     <RelativeLayout
+        android:id="@+id/title_container"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_alwaysShow="true"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index d1bef47..b9e5366 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -9026,10 +9026,10 @@
              {@link android.os.Build.VERSION_CODES#N} and not used in previous versions. -->
         <attr name="supportsLocalInteraction" format="boolean" />
         <!-- The service that provides {@link android.service.voice.HotwordDetectionService}.
-             @hide @SystemApi -->
+             Expect a component name to be provided. @hide @SystemApi -->
         <attr name="hotwordDetectionService" format="string" />
         <!-- The service that provides {@link android.service.voice.VisualQueryDetectionService}.
-             @hide @SystemApi -->
+             Expect a component name to be provided. @hide @SystemApi -->
         <attr name="visualQueryDetectionService" format="string" />
 
     </declare-styleable>
diff --git a/core/tests/coretests/src/android/animation/AnimatorSetCallsTest.java b/core/tests/coretests/src/android/animation/AnimatorSetCallsTest.java
new file mode 100644
index 0000000..fda08ff
--- /dev/null
+++ b/core/tests/coretests/src/android/animation/AnimatorSetCallsTest.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2023 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.animation;
+
+import static org.junit.Assert.assertEquals;
+
+import android.util.PollingCheck;
+import android.view.View;
+
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+import androidx.test.filters.MediumTest;
+
+import com.android.frameworks.coretests.R;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+@MediumTest
+public class AnimatorSetCallsTest {
+    @Rule
+    public final ActivityScenarioRule<AnimatorSetActivity> mRule =
+            new ActivityScenarioRule<>(AnimatorSetActivity.class);
+
+    private AnimatorSetActivity mActivity;
+    private AnimatorSet mSet1;
+    private CountListener mListener1;
+    private CountListener mListener2;
+    private CountListener mListener3;
+
+    @Before
+    public void setUp() throws Exception {
+        mRule.getScenario().onActivity((activity) -> {
+            mActivity = activity;
+            View square = mActivity.findViewById(R.id.square1);
+
+            mSet1 = new AnimatorSet();
+            mListener1 = new CountListener();
+            mSet1.addListener(mListener1);
+            mSet1.addPauseListener(mListener1);
+
+            AnimatorSet set2 = new AnimatorSet();
+            mListener2 = new CountListener();
+            set2.addListener(mListener2);
+            set2.addPauseListener(mListener2);
+
+            ObjectAnimator anim = ObjectAnimator.ofFloat(square, "translationX", 0f, 100f);
+            mListener3 = new CountListener();
+            anim.addListener(mListener3);
+            anim.addPauseListener(mListener3);
+            anim.setDuration(1);
+
+            set2.play(anim);
+            mSet1.play(set2);
+        });
+    }
+
+    @Test
+    public void startEndCalledOnChildren() {
+        mRule.getScenario().onActivity((a) -> mSet1.start());
+        waitForOnUiThread(() -> mListener1.endForward > 0);
+
+        // only startForward and endForward should have been called once
+        mListener1.assertValues(
+                1, 0, 1, 0, 0, 0, 0, 0
+        );
+        mListener2.assertValues(
+                1, 0, 1, 0, 0, 0, 0, 0
+        );
+        mListener3.assertValues(
+                1, 0, 1, 0, 0, 0, 0, 0
+        );
+    }
+
+    @Test
+    public void cancelCalledOnChildren() {
+        mRule.getScenario().onActivity((a) -> {
+            mSet1.start();
+            mSet1.cancel();
+        });
+        waitForOnUiThread(() -> mListener1.endForward > 0);
+
+        // only startForward and endForward should have been called once
+        mListener1.assertValues(
+                1, 0, 1, 0, 1, 0, 0, 0
+        );
+        mListener2.assertValues(
+                1, 0, 1, 0, 1, 0, 0, 0
+        );
+        mListener3.assertValues(
+                1, 0, 1, 0, 1, 0, 0, 0
+        );
+    }
+
+    @Test
+    public void startEndReversedCalledOnChildren() {
+        mRule.getScenario().onActivity((a) -> mSet1.reverse());
+        waitForOnUiThread(() -> mListener1.endReverse > 0);
+
+        // only startForward and endForward should have been called once
+        mListener1.assertValues(
+                0, 1, 0, 1, 0, 0, 0, 0
+        );
+        mListener2.assertValues(
+                0, 1, 0, 1, 0, 0, 0, 0
+        );
+        mListener3.assertValues(
+                0, 1, 0, 1, 0, 0, 0, 0
+        );
+    }
+
+    @Test
+    public void pauseResumeCalledOnChildren() {
+        mRule.getScenario().onActivity((a) -> {
+            mSet1.start();
+            mSet1.pause();
+        });
+        waitForOnUiThread(() -> mListener1.pause > 0);
+
+        // only startForward and pause should have been called once
+        mListener1.assertValues(
+                1, 0, 0, 0, 0, 0, 1, 0
+        );
+        mListener2.assertValues(
+                1, 0, 0, 0, 0, 0, 1, 0
+        );
+        mListener3.assertValues(
+                1, 0, 0, 0, 0, 0, 1, 0
+        );
+
+        mRule.getScenario().onActivity((a) -> mSet1.resume());
+        waitForOnUiThread(() -> mListener1.endForward > 0);
+
+        // resume and endForward should have been called once
+        mListener1.assertValues(
+                1, 0, 1, 0, 0, 0, 1, 1
+        );
+        mListener2.assertValues(
+                1, 0, 1, 0, 0, 0, 1, 1
+        );
+        mListener3.assertValues(
+                1, 0, 1, 0, 0, 0, 1, 1
+        );
+    }
+
+    private void waitForOnUiThread(PollingCheck.PollingCheckCondition condition) {
+        final boolean[] value = new boolean[1];
+        PollingCheck.waitFor(() -> {
+            mActivity.runOnUiThread(() -> value[0] = condition.canProceed());
+            return value[0];
+        });
+    }
+
+    private static class CountListener implements Animator.AnimatorListener,
+            Animator.AnimatorPauseListener {
+        public int startNoParam;
+        public int endNoParam;
+        public int startReverse;
+        public int startForward;
+        public int endForward;
+        public int endReverse;
+        public int cancel;
+        public int repeat;
+        public int pause;
+        public int resume;
+
+        public void assertValues(
+                int startForward,
+                int startReverse,
+                int endForward,
+                int endReverse,
+                int cancel,
+                int repeat,
+                int pause,
+                int resume
+        ) {
+            assertEquals(0, startNoParam);
+            assertEquals(0, endNoParam);
+            assertEquals(startForward, this.startForward);
+            assertEquals(startReverse, this.startReverse);
+            assertEquals(endForward, this.endForward);
+            assertEquals(endReverse, this.endReverse);
+            assertEquals(cancel, this.cancel);
+            assertEquals(repeat, this.repeat);
+            assertEquals(pause, this.pause);
+            assertEquals(resume, this.resume);
+        }
+
+        @Override
+        public void onAnimationStart(Animator animation, boolean isReverse) {
+            if (isReverse) {
+                startReverse++;
+            } else {
+                startForward++;
+            }
+        }
+
+        @Override
+        public void onAnimationEnd(Animator animation, boolean isReverse) {
+            if (isReverse) {
+                endReverse++;
+            } else {
+                endForward++;
+            }
+        }
+
+        @Override
+        public void onAnimationStart(Animator animation) {
+            startNoParam++;
+        }
+
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            endNoParam++;
+        }
+
+        @Override
+        public void onAnimationCancel(Animator animation) {
+            cancel++;
+        }
+
+        @Override
+        public void onAnimationRepeat(Animator animation) {
+            repeat++;
+        }
+
+        @Override
+        public void onAnimationPause(Animator animation) {
+            pause++;
+        }
+
+        @Override
+        public void onAnimationResume(Animator animation) {
+            resume++;
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/animation/ViewPropertyAnimatorTest.java b/core/tests/coretests/src/android/animation/ViewPropertyAnimatorTest.java
index 81cd4da..8cc88ea 100644
--- a/core/tests/coretests/src/android/animation/ViewPropertyAnimatorTest.java
+++ b/core/tests/coretests/src/android/animation/ViewPropertyAnimatorTest.java
@@ -135,11 +135,15 @@
      * @throws Exception
      */
     @Before
-    public void setUp() throws Exception {
+    public void setUp() throws Throwable {
         final BasicAnimatorActivity activity = mActivityRule.getActivity();
         Button button = activity.findViewById(R.id.animatingButton);
 
         mAnimator = button.animate().x(100).y(100);
+        mActivityRule.runOnUiThread(() -> {
+            mAnimator.start();
+            mAnimator.cancel();
+        });
 
         // mListener is the main testing mechanism of this file. The asserts of each test
         // are embedded in the listener callbacks that it implements.
diff --git a/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java b/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java
index a663095..58cfc66 100644
--- a/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java
@@ -16,6 +16,12 @@
 
 package com.android.internal.app;
 
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertNull;
@@ -53,6 +59,7 @@
 import androidx.test.rule.ActivityTestRule;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.internal.R;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 
@@ -140,6 +147,8 @@
     @Test
     public void forwardToManagedProfile_canForward_sendIntent() throws Exception {
         sComponentName = FORWARD_TO_MANAGED_PROFILE_COMPONENT_NAME;
+        sActivityName = "MyTestActivity";
+        sPackageName = "test.package.name";
 
         // Intent can be forwarded.
         when(mIPm.canForwardTo(
@@ -160,7 +169,13 @@
         verify(mIPm).canForwardTo(intentCaptor.capture(), eq(TYPE_PLAIN_TEXT), anyInt(), anyInt());
         assertEquals(Intent.ACTION_SEND, intentCaptor.getValue().getAction());
 
-        assertEquals(Intent.ACTION_SEND, intentCaptor.getValue().getAction());
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+        onView(withId(R.id.icon)).check(matches(isDisplayed()));
+        onView(withId(R.id.open_cross_profile)).check(matches(isDisplayed()));
+        onView(withId(R.id.use_same_profile_browser)).check(matches(isDisplayed()));
+        onView(withId(R.id.button_open)).perform(click());
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
         assertNotNull(activity.mStartActivityIntent);
         assertEquals(Intent.ACTION_SEND, activity.mStartActivityIntent.getAction());
         assertNull(activity.mStartActivityIntent.getPackage());
@@ -250,6 +265,8 @@
     @Test
     public void forwardToManagedProfile_canForward_selectorIntent() throws Exception {
         sComponentName = FORWARD_TO_MANAGED_PROFILE_COMPONENT_NAME;
+        sActivityName = "MyTestActivity";
+        sPackageName = "test.package.name";
 
         // Intent can be forwarded.
         when(mIPm.canForwardTo(
@@ -264,6 +281,7 @@
         // Create selector intent.
         Intent intent = Intent.makeMainSelectorActivity(
                 Intent.ACTION_VIEW, Intent.CATEGORY_BROWSABLE);
+
         IntentForwarderWrapperActivity activity = mActivityRule.launchActivity(intent);
 
         ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
@@ -271,6 +289,13 @@
                 intentCaptor.capture(), nullable(String.class), anyInt(), anyInt());
         assertEquals(Intent.ACTION_VIEW, intentCaptor.getValue().getAction());
 
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+        onView(withId(R.id.icon)).check(matches(isDisplayed()));
+        onView(withId(R.id.open_cross_profile)).check(matches(isDisplayed()));
+        onView(withId(R.id.use_same_profile_browser)).check(matches(isDisplayed()));
+        onView(withId(R.id.button_open)).perform(click());
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
         assertNotNull(activity.mStartActivityIntent);
         assertEquals(Intent.ACTION_MAIN, activity.mStartActivityIntent.getAction());
         assertNull(activity.mStartActivityIntent.getPackage());
@@ -608,7 +633,7 @@
     }
 
     private void setupShouldSkipDisclosureTest() throws RemoteException {
-        sComponentName = FORWARD_TO_MANAGED_PROFILE_COMPONENT_NAME;
+        sComponentName = FORWARD_TO_PARENT_COMPONENT_NAME;
         sActivityName = "MyTestActivity";
         sPackageName = "test.package.name";
         Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED,
@@ -619,6 +644,7 @@
         profiles.add(CURRENT_USER_INFO);
         profiles.add(MANAGED_PROFILE_INFO);
         when(mUserManager.getProfiles(anyInt())).thenReturn(profiles);
+        when(mUserManager.getProfileParent(anyInt())).thenReturn(CURRENT_USER_INFO);
         // Intent can be forwarded.
         when(mIPm.canForwardTo(
                 any(Intent.class), nullable(String.class), anyInt(), anyInt())).thenReturn(true);
@@ -654,6 +680,11 @@
         }
 
         @Override
+        public Context createContextAsUser(UserHandle user, int flags) {
+            return this;
+        }
+
+        @Override
         protected MetricsLogger getMetricsLogger() {
             return mMetricsLogger;
         }
diff --git a/data/etc/preinstalled-packages-platform.xml b/data/etc/preinstalled-packages-platform.xml
index ff8d96d..421bc25 100644
--- a/data/etc/preinstalled-packages-platform.xml
+++ b/data/etc/preinstalled-packages-platform.xml
@@ -102,11 +102,29 @@
 to pre-existing users, but cannot uninstall pre-existing system packages from pre-existing users.
 -->
 <config>
+    <!--  Bluetooth (com.android.btservices apex) - visible on the sharesheet -->
+    <install-in-user-type package="com.android.bluetooth">
+        <install-in user-type="SYSTEM" />
+        <install-in user-type="FULL" />
+        <install-in user-type="PROFILE" />
+        <do-not-install-in user-type="android.os.usertype.profile.CLONE" />
+    </install-in-user-type>
+
+    <!--  Settings (Settings app) -->
+    <install-in-user-type package="com.android.settings">
+        <install-in user-type="SYSTEM" />
+        <install-in user-type="FULL" />
+        <install-in user-type="PROFILE" />
+    </install-in-user-type>
+
+    <!-- Settings Storage (SettingsProvider)  -->
     <install-in-user-type package="com.android.providers.settings">
         <install-in user-type="SYSTEM" />
         <install-in user-type="FULL" />
         <install-in user-type="PROFILE" />
     </install-in-user-type>
+
+    <!-- WallpaperBackup (WallpaperBackup)-->
     <install-in-user-type package="com.android.wallpaperbackup">
         <install-in user-type="FULL" />
     </install-in-user-type>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index d726b67..71050fa 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -506,6 +506,8 @@
         <permission name="android.permission.ACCESS_BROADCAST_RADIO"/>
         <!-- Permission required for CTS test - CtsAmbientContextServiceTestCases -->
         <permission name="android.permission.ACCESS_AMBIENT_CONTEXT_EVENT"/>
+        <!-- Permission required for CTS test - CtsTelephonyProviderTestCases -->
+        <permission name="android.permission.WRITE_APN_SETTINGS"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.statementservice">
diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
index 65923ff..67ca9a1 100644
--- a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
@@ -24,7 +24,11 @@
     </target_preparer>
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
         <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1" />
+        <option name="run-command" value="settings put system show_touches 1" />
+        <option name="run-command" value="settings put system pointer_location 1" />
         <option name="teardown-command" value="settings delete secure show_ime_with_hard_keyboard" />
+        <option name="teardown-command" value="settings delete system show_touches" />
+        <option name="teardown-command" value="settings delete system pointer_location" />
     </target_preparer>
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true"/>
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index b0896da..33c9eac 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -21,7 +21,7 @@
 #ifdef __ANDROID__
 #include "HWUIProperties.sysprop.h"
 #endif
-#include "SkTraceEventCommon.h"
+#include "src/core/SkTraceEventCommon.h"
 
 #include <algorithm>
 #include <cstdlib>
diff --git a/libs/hwui/jni/MaskFilter.cpp b/libs/hwui/jni/MaskFilter.cpp
index 048ce02..cbd4520 100644
--- a/libs/hwui/jni/MaskFilter.cpp
+++ b/libs/hwui/jni/MaskFilter.cpp
@@ -1,6 +1,5 @@
 #include "GraphicsJNI.h"
 #include "SkMaskFilter.h"
-#include "SkBlurMask.h"
 #include "SkBlurMaskFilter.h"
 #include "SkBlurTypes.h"
 #include "SkTableMaskFilter.h"
@@ -11,6 +10,13 @@
     }
 }
 
+// From https://skia.googlesource.com/skia/+/d74c99a3cd5eef5f16b2eb226e6b45fe523c8552/src/core/SkBlurMask.cpp#28
+static constexpr float kBLUR_SIGMA_SCALE = 0.57735f;
+
+static float convertRadiusToSigma(float radius) {
+    return radius > 0 ? kBLUR_SIGMA_SCALE * radius + 0.5f : 0.0f;
+}
+
 class SkMaskFilterGlue {
 public:
     static void destructor(JNIEnv* env, jobject, jlong filterHandle) {
@@ -19,7 +25,7 @@
     }
 
     static jlong createBlur(JNIEnv* env, jobject, jfloat radius, jint blurStyle) {
-        SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(radius);
+        SkScalar sigma = convertRadiusToSigma(radius);
         SkMaskFilter* filter = SkMaskFilter::MakeBlur((SkBlurStyle)blurStyle, sigma).release();
         ThrowIAE_IfNull(env, filter);
         return reinterpret_cast<jlong>(filter);
@@ -34,7 +40,7 @@
             direction[i] = values[i];
         }
 
-        SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(radius);
+        SkScalar sigma = convertRadiusToSigma(radius);
         SkMaskFilter* filter =  SkBlurMaskFilter::MakeEmboss(sigma,
                 direction, ambient, specular).release();
         ThrowIAE_IfNull(env, filter);
diff --git a/media/tests/MediaFrameworkTest/AndroidManifest.xml b/media/tests/MediaFrameworkTest/AndroidManifest.xml
index 41ac285..e886558 100644
--- a/media/tests/MediaFrameworkTest/AndroidManifest.xml
+++ b/media/tests/MediaFrameworkTest/AndroidManifest.xml
@@ -41,7 +41,6 @@
         </activity>
         <activity android:label="Camera2CtsActivity"
              android:name="Camera2SurfaceViewActivity"
-             android:screenOrientation="landscape"
              android:configChanges="keyboardHidden|orientation|screenSize"
              android:showWhenLocked="true"
              android:turnScreenOn="true"
diff --git a/packages/SettingsLib/ActionBarShadow/Android.bp b/packages/SettingsLib/ActionBarShadow/Android.bp
index 9b0e044..bf4c858 100644
--- a/packages/SettingsLib/ActionBarShadow/Android.bp
+++ b/packages/SettingsLib/ActionBarShadow/Android.bp
@@ -22,7 +22,8 @@
     min_sdk_version: "28",
     apex_available: [
         "//apex_available:platform",
-        "com.android.permission",
         "com.android.adservices",
+        "com.android.extservices",
+        "com.android.permission",
     ],
 }
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp b/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp
index 6330848f..e9000a8 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp
@@ -27,6 +27,7 @@
         "//apex_available:platform",
         "com.android.adservices",
         "com.android.cellbroadcast",
+        "com.android.extservices",
         "com.android.permission",
         "com.android.healthfitness",
     ],
diff --git a/packages/SettingsLib/MainSwitchPreference/Android.bp b/packages/SettingsLib/MainSwitchPreference/Android.bp
index 825e6ac..33aa985 100644
--- a/packages/SettingsLib/MainSwitchPreference/Android.bp
+++ b/packages/SettingsLib/MainSwitchPreference/Android.bp
@@ -25,6 +25,7 @@
         "//apex_available:platform",
         "com.android.adservices",
         "com.android.cellbroadcast",
+        "com.android.extservices",
         "com.android.healthfitness",
     ],
 }
diff --git a/packages/SettingsLib/SettingsTheme/Android.bp b/packages/SettingsLib/SettingsTheme/Android.bp
index 09691f1..d26ad1c 100644
--- a/packages/SettingsLib/SettingsTheme/Android.bp
+++ b/packages/SettingsLib/SettingsTheme/Android.bp
@@ -9,18 +9,14 @@
 
 android_library {
     name: "SettingsLibSettingsTheme",
-
     resource_dirs: ["res"],
-
-    static_libs: [
-        "androidx.preference_preference",
-    ],
-
+    static_libs: ["androidx.preference_preference"],
     sdk_version: "system_current",
     min_sdk_version: "21",
     apex_available: [
         "//apex_available:platform",
         "com.android.cellbroadcast",
+        "com.android.extservices",
         "com.android.permission",
         "com.android.adservices",
         "com.android.healthfitness",
diff --git a/packages/SettingsLib/SettingsTransition/Android.bp b/packages/SettingsLib/SettingsTransition/Android.bp
index 7f9014c..0f9f781 100644
--- a/packages/SettingsLib/SettingsTransition/Android.bp
+++ b/packages/SettingsLib/SettingsTransition/Android.bp
@@ -22,6 +22,7 @@
         "//apex_available:platform",
         "com.android.adservices",
         "com.android.cellbroadcast",
+        "com.android.extservices",
         "com.android.permission",
         "com.android.healthfitness",
     ],
diff --git a/packages/SettingsLib/Utils/Android.bp b/packages/SettingsLib/Utils/Android.bp
index 644e990..33ba64a 100644
--- a/packages/SettingsLib/Utils/Android.bp
+++ b/packages/SettingsLib/Utils/Android.bp
@@ -24,8 +24,9 @@
 
         "//apex_available:platform",
         "com.android.adservices",
-        "com.android.permission",
         "com.android.cellbroadcast",
+        "com.android.extservices",
         "com.android.healthfitness",
+        "com.android.permission",
     ],
 }
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 0f67124..82ca63d 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -819,6 +819,9 @@
     <!-- Permission required for CTS test - ActivityCaptureCallbackTests -->
     <uses-permission android:name="android.permission.DETECT_SCREEN_CAPTURE" />
 
+    <!-- Permission required for CTS test - CtsTelephonyProviderTestCases -->
+    <uses-permission android:name="android.permission.WRITE_APN_SETTINGS" />
+
     <application android:label="@string/app_label"
                 android:theme="@android:style/Theme.DeviceDefault.DayNight"
                 android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index addae72..13e2bca 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -683,7 +683,10 @@
     public void onTrustManagedChanged(boolean managed, int userId) {
         Assert.isMainThread();
         mUserTrustIsManaged.put(userId, managed);
-        mUserTrustIsUsuallyManaged.put(userId, mTrustManager.isTrustUsuallyManaged(userId));
+        boolean trustUsuallyManaged = mTrustManager.isTrustUsuallyManaged(userId);
+        mLogger.logTrustUsuallyManagedUpdated(userId, mUserTrustIsUsuallyManaged.get(userId),
+                trustUsuallyManaged, "onTrustManagedChanged");
+        mUserTrustIsUsuallyManaged.put(userId, trustUsuallyManaged);
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
@@ -2350,8 +2353,12 @@
         updateSecondaryLockscreenRequirement(user);
         List<UserInfo> allUsers = mUserManager.getUsers();
         for (UserInfo userInfo : allUsers) {
+            boolean trustUsuallyManaged = mTrustManager.isTrustUsuallyManaged(userInfo.id);
+            mLogger.logTrustUsuallyManagedUpdated(userInfo.id,
+                    mUserTrustIsUsuallyManaged.get(userInfo.id),
+                    trustUsuallyManaged, "init from constructor");
             mUserTrustIsUsuallyManaged.put(userInfo.id,
-                    mTrustManager.isTrustUsuallyManaged(userInfo.id));
+                    trustUsuallyManaged);
         }
         updateAirplaneModeState();
 
@@ -2391,9 +2398,11 @@
     }
 
     private void updateFaceEnrolled(int userId) {
-        mIsFaceEnrolled = mFaceManager != null && !mFaceSensorProperties.isEmpty()
+        Boolean isFaceEnrolled = mFaceManager != null && !mFaceSensorProperties.isEmpty()
                 && mBiometricEnabledForUser.get(userId)
                 && mAuthController.isFaceAuthEnrolled(userId);
+        mIsFaceEnrolled = isFaceEnrolled;
+        mLogger.logFaceEnrolledUpdated(mIsFaceEnrolled, isFaceEnrolled);
     }
 
     public boolean isFaceSupported() {
@@ -3052,9 +3061,13 @@
     @VisibleForTesting
     boolean isUnlockWithFingerprintPossible(int userId) {
         // TODO (b/242022358), make this rely on onEnrollmentChanged event and update it only once.
-        mIsUnlockWithFingerprintPossible.put(userId, mFpm != null
+        boolean fpEnrolled = mFpm != null
                 && !mFingerprintSensorProperties.isEmpty()
-                && !isFingerprintDisabled(userId) && mFpm.hasEnrolledTemplates(userId));
+                && !isFingerprintDisabled(userId) && mFpm.hasEnrolledTemplates(userId);
+        mLogger.logFpEnrolledUpdated(userId,
+                mIsUnlockWithFingerprintPossible.getOrDefault(userId, false),
+                fpEnrolled);
+        mIsUnlockWithFingerprintPossible.put(userId, fpEnrolled);
         return mIsUnlockWithFingerprintPossible.get(userId);
     }
 
@@ -3170,7 +3183,10 @@
     void handleUserSwitching(int userId, IRemoteCallback reply) {
         Assert.isMainThread();
         clearBiometricRecognized();
-        mUserTrustIsUsuallyManaged.put(userId, mTrustManager.isTrustUsuallyManaged(userId));
+        boolean trustUsuallyManaged = mTrustManager.isTrustUsuallyManaged(userId);
+        mLogger.logTrustUsuallyManagedUpdated(userId, mUserTrustIsUsuallyManaged.get(userId),
+                trustUsuallyManaged, "userSwitching");
+        mUserTrustIsUsuallyManaged.put(userId, trustUsuallyManaged);
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
index c414c08..7707e1f 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
@@ -26,6 +26,7 @@
 import com.android.keyguard.KeyguardListenModel
 import com.android.keyguard.KeyguardUpdateMonitorCallback
 import com.android.keyguard.TrustGrantFlags
+import com.android.systemui.log.dagger.KeyguardUpdateMonitorLog
 import com.android.systemui.plugins.log.LogBuffer
 import com.android.systemui.plugins.log.LogLevel
 import com.android.systemui.plugins.log.LogLevel.DEBUG
@@ -33,18 +34,15 @@
 import com.android.systemui.plugins.log.LogLevel.INFO
 import com.android.systemui.plugins.log.LogLevel.VERBOSE
 import com.android.systemui.plugins.log.LogLevel.WARNING
-import com.android.systemui.log.dagger.KeyguardUpdateMonitorLog
 import com.google.errorprone.annotations.CompileTimeConstant
 import javax.inject.Inject
 
 private const val TAG = "KeyguardUpdateMonitorLog"
 
-/**
- * Helper class for logging for [com.android.keyguard.KeyguardUpdateMonitor]
- */
-class KeyguardUpdateMonitorLogger @Inject constructor(
-        @KeyguardUpdateMonitorLog private val logBuffer: LogBuffer
-) {
+/** Helper class for logging for [com.android.keyguard.KeyguardUpdateMonitor] */
+class KeyguardUpdateMonitorLogger
+@Inject
+constructor(@KeyguardUpdateMonitorLog private val logBuffer: LogBuffer) {
     fun d(@CompileTimeConstant msg: String) = log(msg, DEBUG)
 
     fun e(@CompileTimeConstant msg: String) = log(msg, ERROR)
@@ -56,15 +54,16 @@
     fun log(@CompileTimeConstant msg: String, level: LogLevel) = logBuffer.log(TAG, level, msg)
 
     fun logActiveUnlockTriggered(reason: String?) {
-        logBuffer.log("ActiveUnlock", DEBUG,
-                { str1 = reason },
-                { "initiate active unlock triggerReason=$str1" })
+        logBuffer.log(
+            "ActiveUnlock",
+            DEBUG,
+            { str1 = reason },
+            { "initiate active unlock triggerReason=$str1" }
+        )
     }
 
     fun logAuthInterruptDetected(active: Boolean) {
-        logBuffer.log(TAG, DEBUG,
-                { bool1 = active },
-                { "onAuthInterruptDetected($bool1)" })
+        logBuffer.log(TAG, DEBUG, { bool1 = active }, { "onAuthInterruptDetected($bool1)" })
     }
 
     fun logBroadcastReceived(action: String?) {
@@ -72,9 +71,12 @@
     }
 
     fun logDeviceProvisionedState(deviceProvisioned: Boolean) {
-        logBuffer.log(TAG, DEBUG,
-                { bool1 = deviceProvisioned },
-                { "DEVICE_PROVISIONED state = $bool1" })
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            { bool1 = deviceProvisioned },
+            { "DEVICE_PROVISIONED state = $bool1" }
+        )
     }
 
     fun logException(ex: Exception, @CompileTimeConstant logMsg: String) {
@@ -82,46 +84,56 @@
     }
 
     fun logFaceAcquired(acquireInfo: Int) {
-        logBuffer.log(TAG, DEBUG,
-                { int1 = acquireInfo },
-                { "Face acquired acquireInfo=$int1" })
+        logBuffer.log(TAG, DEBUG, { int1 = acquireInfo }, { "Face acquired acquireInfo=$int1" })
     }
 
     fun logFaceAuthDisabledForUser(userId: Int) {
-        logBuffer.log(TAG, DEBUG,
-                { int1 = userId },
-                { "Face authentication disabled by DPM for userId: $int1" })
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            { int1 = userId },
+            { "Face authentication disabled by DPM for userId: $int1" }
+        )
     }
     fun logFaceAuthError(msgId: Int, originalErrMsg: String) {
-        logBuffer.log(TAG, DEBUG, {
-                    str1 = originalErrMsg
-                    int1 = msgId
-                }, { "Face error received: $str1 msgId= $int1" })
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                str1 = originalErrMsg
+                int1 = msgId
+            },
+            { "Face error received: $str1 msgId= $int1" }
+        )
     }
 
     fun logFaceAuthForWrongUser(authUserId: Int) {
-        logBuffer.log(TAG, DEBUG,
-                { int1 = authUserId },
-                { "Face authenticated for wrong user: $int1" })
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            { int1 = authUserId },
+            { "Face authenticated for wrong user: $int1" }
+        )
     }
 
     fun logFaceAuthHelpMsg(msgId: Int, helpMsg: String?) {
-        logBuffer.log(TAG, DEBUG, {
-                    int1 = msgId
-                    str1 = helpMsg
-                }, { "Face help received, msgId: $int1 msg: $str1" })
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                int1 = msgId
+                str1 = helpMsg
+            },
+            { "Face help received, msgId: $int1 msg: $str1" }
+        )
     }
 
     fun logFaceAuthRequested(reason: String?) {
-        logBuffer.log(TAG, DEBUG, {
-            str1 = reason
-        }, { "requestFaceAuth() reason=$str1" })
+        logBuffer.log(TAG, DEBUG, { str1 = reason }, { "requestFaceAuth() reason=$str1" })
     }
 
     fun logFaceAuthSuccess(userId: Int) {
-        logBuffer.log(TAG, DEBUG,
-                { int1 = userId },
-                { "Face auth succeeded for user $int1" })
+        logBuffer.log(TAG, DEBUG, { int1 = userId }, { "Face auth succeeded for user $int1" })
     }
 
     fun logFaceLockoutReset(@LockoutMode mode: Int) {
@@ -133,21 +145,30 @@
     }
 
     fun logFaceUnlockPossible(isFaceUnlockPossible: Boolean) {
-        logBuffer.log(TAG, DEBUG,
-                { bool1 = isFaceUnlockPossible },
-                {"isUnlockWithFacePossible: $bool1"})
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            { bool1 = isFaceUnlockPossible },
+            { "isUnlockWithFacePossible: $bool1" }
+        )
     }
 
     fun logFingerprintAuthForWrongUser(authUserId: Int) {
-        logBuffer.log(TAG, DEBUG,
-                { int1 = authUserId },
-                { "Fingerprint authenticated for wrong user: $int1" })
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            { int1 = authUserId },
+            { "Fingerprint authenticated for wrong user: $int1" }
+        )
     }
 
     fun logFingerprintDisabledForUser(userId: Int) {
-        logBuffer.log(TAG, DEBUG,
-                { int1 = userId },
-                { "Fingerprint disabled by DPM for userId: $int1" })
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            { int1 = userId },
+            { "Fingerprint disabled by DPM for userId: $int1" }
+        )
     }
 
     fun logFingerprintLockoutReset(@LockoutMode mode: Int) {
@@ -155,42 +176,63 @@
     }
 
     fun logFingerprintRunningState(fingerprintRunningState: Int) {
-        logBuffer.log(TAG, DEBUG,
-                { int1 = fingerprintRunningState },
-                { "fingerprintRunningState: $int1" })
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            { int1 = fingerprintRunningState },
+            { "fingerprintRunningState: $int1" }
+        )
     }
 
     fun logFingerprintSuccess(userId: Int, isStrongBiometric: Boolean) {
-        logBuffer.log(TAG, DEBUG, {
-            int1 = userId
-            bool1 = isStrongBiometric
-        }, {"Fingerprint auth successful: userId: $int1, isStrongBiometric: $bool1"})
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                int1 = userId
+                bool1 = isStrongBiometric
+            },
+            { "Fingerprint auth successful: userId: $int1, isStrongBiometric: $bool1" }
+        )
     }
 
     fun logFingerprintError(msgId: Int, originalErrMsg: String) {
-        logBuffer.log(TAG, DEBUG, {
-            str1 = originalErrMsg
-            int1 = msgId
-        }, { "Fingerprint error received: $str1 msgId= $int1" })
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                str1 = originalErrMsg
+                int1 = msgId
+            },
+            { "Fingerprint error received: $str1 msgId= $int1" }
+        )
     }
 
     fun logInvalidSubId(subId: Int) {
-        logBuffer.log(TAG, INFO,
-                { int1 = subId },
-                { "Previously active sub id $int1 is now invalid, will remove" })
+        logBuffer.log(
+            TAG,
+            INFO,
+            { int1 = subId },
+            { "Previously active sub id $int1 is now invalid, will remove" }
+        )
     }
 
     fun logPrimaryKeyguardBouncerChanged(
-            primaryBouncerIsOrWillBeShowing: Boolean,
-            primaryBouncerFullyShown: Boolean
+        primaryBouncerIsOrWillBeShowing: Boolean,
+        primaryBouncerFullyShown: Boolean
     ) {
-        logBuffer.log(TAG, DEBUG, {
-            bool1 = primaryBouncerIsOrWillBeShowing
-            bool2 = primaryBouncerFullyShown
-        }, {
-            "handlePrimaryBouncerChanged " +
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                bool1 = primaryBouncerIsOrWillBeShowing
+                bool2 = primaryBouncerFullyShown
+            },
+            {
+                "handlePrimaryBouncerChanged " +
                     "primaryBouncerIsOrWillBeShowing=$bool1 primaryBouncerFullyShown=$bool2"
-        })
+            }
+        )
     }
 
     fun logKeyguardListenerModel(model: KeyguardListenModel) {
@@ -198,98 +240,134 @@
     }
 
     fun logKeyguardShowingChanged(showing: Boolean, occluded: Boolean, visible: Boolean) {
-        logBuffer.log(TAG, DEBUG, {
-            bool1 = showing
-            bool2 = occluded
-            bool3 = visible
-        }, {
-            "keyguardShowingChanged(showing=$bool1 occluded=$bool2 visible=$bool3)"
-        })
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                bool1 = showing
+                bool2 = occluded
+                bool3 = visible
+            },
+            { "keyguardShowingChanged(showing=$bool1 occluded=$bool2 visible=$bool3)" }
+        )
     }
 
     fun logMissingSupervisorAppError(userId: Int) {
-        logBuffer.log(TAG, ERROR,
-                { int1 = userId },
-                { "No Profile Owner or Device Owner supervision app found for User $int1" })
+        logBuffer.log(
+            TAG,
+            ERROR,
+            { int1 = userId },
+            { "No Profile Owner or Device Owner supervision app found for User $int1" }
+        )
     }
 
     fun logPhoneStateChanged(newState: String?) {
-        logBuffer.log(TAG, DEBUG,
-                { str1 = newState },
-                { "handlePhoneStateChanged($str1)" })
+        logBuffer.log(TAG, DEBUG, { str1 = newState }, { "handlePhoneStateChanged($str1)" })
     }
 
     fun logRegisterCallback(callback: KeyguardUpdateMonitorCallback?) {
-        logBuffer.log(TAG, VERBOSE,
-                { str1 = "$callback" },
-                { "*** register callback for $str1" })
+        logBuffer.log(TAG, VERBOSE, { str1 = "$callback" }, { "*** register callback for $str1" })
     }
 
     fun logRetryingAfterFaceHwUnavailable(retryCount: Int) {
-        logBuffer.log(TAG, WARNING,
-                { int1 = retryCount },
-                { "Retrying face after HW unavailable, attempt $int1" })
+        logBuffer.log(
+            TAG,
+            WARNING,
+            { int1 = retryCount },
+            { "Retrying face after HW unavailable, attempt $int1" }
+        )
     }
 
     fun logRetryAfterFpErrorWithDelay(msgId: Int, errString: String?, delay: Int) {
-        logBuffer.log(TAG, DEBUG, {
-            int1 = msgId
-            int2 = delay
-            str1 = "$errString"
-        }, {
-            "Fingerprint scheduling retry auth after $int2 ms due to($int1) -> $str1"
-        })
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                int1 = msgId
+                int2 = delay
+                str1 = "$errString"
+            },
+            { "Fingerprint scheduling retry auth after $int2 ms due to($int1) -> $str1" }
+        )
     }
 
     fun logRetryAfterFpHwUnavailable(retryCount: Int) {
-        logBuffer.log(TAG, WARNING,
-                { int1 = retryCount },
-                { "Retrying fingerprint attempt: $int1" })
+        logBuffer.log(
+            TAG,
+            WARNING,
+            { int1 = retryCount },
+            { "Retrying fingerprint attempt: $int1" }
+        )
     }
 
     fun logSendPrimaryBouncerChanged(
         primaryBouncerIsOrWillBeShowing: Boolean,
         primaryBouncerFullyShown: Boolean,
     ) {
-        logBuffer.log(TAG, DEBUG, {
-            bool1 = primaryBouncerIsOrWillBeShowing
-            bool2 = primaryBouncerFullyShown
-        }, {
-            "sendPrimaryBouncerChanged primaryBouncerIsOrWillBeShowing=$bool1 " +
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                bool1 = primaryBouncerIsOrWillBeShowing
+                bool2 = primaryBouncerFullyShown
+            },
+            {
+                "sendPrimaryBouncerChanged primaryBouncerIsOrWillBeShowing=$bool1 " +
                     "primaryBouncerFullyShown=$bool2"
-        })
+            }
+        )
     }
 
     fun logServiceStateChange(subId: Int, serviceState: ServiceState?) {
-        logBuffer.log(TAG, DEBUG, {
-            int1 = subId
-            str1 = "$serviceState"
-        }, { "handleServiceStateChange(subId=$int1, serviceState=$str1)" })
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                int1 = subId
+                str1 = "$serviceState"
+            },
+            { "handleServiceStateChange(subId=$int1, serviceState=$str1)" }
+        )
     }
 
     fun logServiceStateIntent(action: String?, serviceState: ServiceState?, subId: Int) {
-        logBuffer.log(TAG, VERBOSE, {
-            str1 = action
-            str2 = "$serviceState"
-            int1 = subId
-        }, { "action $str1 serviceState=$str2 subId=$int1" })
+        logBuffer.log(
+            TAG,
+            VERBOSE,
+            {
+                str1 = action
+                str2 = "$serviceState"
+                int1 = subId
+            },
+            { "action $str1 serviceState=$str2 subId=$int1" }
+        )
     }
 
     fun logSimState(subId: Int, slotId: Int, state: Int) {
-        logBuffer.log(TAG, DEBUG, {
-            int1 = subId
-            int2 = slotId
-            long1 = state.toLong()
-        }, { "handleSimStateChange(subId=$int1, slotId=$int2, state=$long1)" })
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                int1 = subId
+                int2 = slotId
+                long1 = state.toLong()
+            },
+            { "handleSimStateChange(subId=$int1, slotId=$int2, state=$long1)" }
+        )
     }
 
     fun logSimStateFromIntent(action: String?, extraSimState: String?, slotId: Int, subId: Int) {
-        logBuffer.log(TAG, VERBOSE, {
-            str1 = action
-            str2 = extraSimState
-            int1 = slotId
-            int2 = subId
-        }, { "action $str1 state: $str2 slotId: $int1 subid: $int2" })
+        logBuffer.log(
+            TAG,
+            VERBOSE,
+            {
+                str1 = action
+                str2 = extraSimState
+                int1 = slotId
+                int2 = subId
+            },
+            { "action $str1 state: $str2 slotId: $int1 subid: $int2" }
+        )
     }
 
     fun logSimUnlocked(subId: Int) {
@@ -297,78 +375,98 @@
     }
 
     fun logStartedListeningForFace(faceRunningState: Int, faceAuthUiEvent: FaceAuthUiEvent) {
-        logBuffer.log(TAG, VERBOSE, {
-            int1 = faceRunningState
-            str1 = faceAuthUiEvent.reason
-            str2 = faceAuthUiEvent.extraInfoToString()
-        }, { "startListeningForFace(): $int1, reason: $str1 $str2" })
+        logBuffer.log(
+            TAG,
+            VERBOSE,
+            {
+                int1 = faceRunningState
+                str1 = faceAuthUiEvent.reason
+                str2 = faceAuthUiEvent.extraInfoToString()
+            },
+            { "startListeningForFace(): $int1, reason: $str1 $str2" }
+        )
     }
 
     fun logStartedListeningForFaceFromWakeUp(faceRunningState: Int, @WakeReason pmWakeReason: Int) {
-        logBuffer.log(TAG, VERBOSE, {
-            int1 = faceRunningState
-            str1 = PowerManager.wakeReasonToString(pmWakeReason)
-        }, { "startListeningForFace(): $int1, reason: wakeUp-$str1" })
+        logBuffer.log(
+            TAG,
+            VERBOSE,
+            {
+                int1 = faceRunningState
+                str1 = PowerManager.wakeReasonToString(pmWakeReason)
+            },
+            { "startListeningForFace(): $int1, reason: wakeUp-$str1" }
+        )
     }
 
     fun logStoppedListeningForFace(faceRunningState: Int, faceAuthReason: String) {
-        logBuffer.log(TAG, VERBOSE, {
-            int1 = faceRunningState
-            str1 = faceAuthReason
-        }, { "stopListeningForFace(): currentFaceRunningState: $int1, reason: $str1" })
+        logBuffer.log(
+            TAG,
+            VERBOSE,
+            {
+                int1 = faceRunningState
+                str1 = faceAuthReason
+            },
+            { "stopListeningForFace(): currentFaceRunningState: $int1, reason: $str1" }
+        )
     }
 
     fun logSubInfo(subInfo: SubscriptionInfo?) {
-        logBuffer.log(TAG, VERBOSE,
-                { str1 = "$subInfo" },
-                { "SubInfo:$str1" })
+        logBuffer.log(TAG, VERBOSE, { str1 = "$subInfo" }, { "SubInfo:$str1" })
     }
 
     fun logTimeFormatChanged(newTimeFormat: String?) {
-        logBuffer.log(TAG, DEBUG,
-                { str1 = newTimeFormat },
-                { "handleTimeFormatUpdate timeFormat=$str1" })
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            { str1 = newTimeFormat },
+            { "handleTimeFormatUpdate timeFormat=$str1" }
+        )
     }
     fun logUdfpsPointerDown(sensorId: Int) {
-        logBuffer.log(TAG, DEBUG,
-                { int1 = sensorId },
-                { "onUdfpsPointerDown, sensorId: $int1" })
+        logBuffer.log(TAG, DEBUG, { int1 = sensorId }, { "onUdfpsPointerDown, sensorId: $int1" })
     }
 
     fun logUdfpsPointerUp(sensorId: Int) {
-        logBuffer.log(TAG, DEBUG,
-                { int1 = sensorId },
-                { "onUdfpsPointerUp, sensorId: $int1" })
+        logBuffer.log(TAG, DEBUG, { int1 = sensorId }, { "onUdfpsPointerUp, sensorId: $int1" })
     }
 
     fun logUnexpectedFaceCancellationSignalState(faceRunningState: Int, unlockPossible: Boolean) {
-        logBuffer.log(TAG, ERROR, {
-                    int1 = faceRunningState
-                    bool1 = unlockPossible
-                }, {
-                    "Cancellation signal is not null, high chance of bug in " +
-                            "face auth lifecycle management. " +
-                            "Face state: $int1, unlockPossible: $bool1"
-                })
+        logBuffer.log(
+            TAG,
+            ERROR,
+            {
+                int1 = faceRunningState
+                bool1 = unlockPossible
+            },
+            {
+                "Cancellation signal is not null, high chance of bug in " +
+                    "face auth lifecycle management. " +
+                    "Face state: $int1, unlockPossible: $bool1"
+            }
+        )
     }
 
     fun logUnexpectedFpCancellationSignalState(
         fingerprintRunningState: Int,
         unlockPossible: Boolean
     ) {
-        logBuffer.log(TAG, ERROR, {
-                    int1 = fingerprintRunningState
-                    bool1 = unlockPossible
-                }, {
-                    "Cancellation signal is not null, high chance of bug in " +
-                            "fp auth lifecycle management. FP state: $int1, unlockPossible: $bool1"
-                })
+        logBuffer.log(
+            TAG,
+            ERROR,
+            {
+                int1 = fingerprintRunningState
+                bool1 = unlockPossible
+            },
+            {
+                "Cancellation signal is not null, high chance of bug in " +
+                    "fp auth lifecycle management. FP state: $int1, unlockPossible: $bool1"
+            }
+        )
     }
 
     fun logUnregisterCallback(callback: KeyguardUpdateMonitorCallback?) {
-        logBuffer.log(TAG, VERBOSE,
-                { str1 = "$callback" },
-                { "*** unregister callback for $str1" })
+        logBuffer.log(TAG, VERBOSE, { str1 = "$callback" }, { "*** unregister callback for $str1" })
     }
 
     fun logUserRequestedUnlock(
@@ -376,75 +474,149 @@
         reason: String?,
         dismissKeyguard: Boolean
     ) {
-        logBuffer.log("ActiveUnlock", DEBUG, {
-                    str1 = requestOrigin?.name
-                    str2 = reason
-                    bool1 = dismissKeyguard
-                }, { "reportUserRequestedUnlock origin=$str1 reason=$str2 dismissKeyguard=$bool1" })
+        logBuffer.log(
+            "ActiveUnlock",
+            DEBUG,
+            {
+                str1 = requestOrigin?.name
+                str2 = reason
+                bool1 = dismissKeyguard
+            },
+            { "reportUserRequestedUnlock origin=$str1 reason=$str2 dismissKeyguard=$bool1" }
+        )
     }
 
     fun logTrustGrantedWithFlags(
-            flags: Int,
-            newlyUnlocked: Boolean,
-            userId: Int,
-            message: String?
+        flags: Int,
+        newlyUnlocked: Boolean,
+        userId: Int,
+        message: String?
     ) {
-        logBuffer.log(TAG, DEBUG, {
-            int1 = flags
-            bool1 = newlyUnlocked
-            int2 = userId
-            str1 = message
-        }, { "trustGrantedWithFlags[user=$int2] newlyUnlocked=$bool1 " +
-                "flags=${TrustGrantFlags(int1)} message=$str1" })
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                int1 = flags
+                bool1 = newlyUnlocked
+                int2 = userId
+                str1 = message
+            },
+            {
+                "trustGrantedWithFlags[user=$int2] newlyUnlocked=$bool1 " +
+                    "flags=${TrustGrantFlags(int1)} message=$str1"
+            }
+        )
     }
 
-    fun logTrustChanged(
-            wasTrusted: Boolean,
-            isNowTrusted: Boolean,
-            userId: Int
-    ) {
-        logBuffer.log(TAG, DEBUG, {
-            bool1 = wasTrusted
-            bool2 = isNowTrusted
-            int1 = userId
-        }, { "onTrustChanged[user=$int1] wasTrusted=$bool1 isNowTrusted=$bool2" })
+    fun logTrustChanged(wasTrusted: Boolean, isNowTrusted: Boolean, userId: Int) {
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                bool1 = wasTrusted
+                bool2 = isNowTrusted
+                int1 = userId
+            },
+            { "onTrustChanged[user=$int1] wasTrusted=$bool1 isNowTrusted=$bool2" }
+        )
     }
 
     fun logKeyguardStateUpdate(
-            secure: Boolean,
-            canDismissLockScreen: Boolean,
-            trusted: Boolean,
-            trustManaged: Boolean
-
+        secure: Boolean,
+        canDismissLockScreen: Boolean,
+        trusted: Boolean,
+        trustManaged: Boolean
     ) {
-        logBuffer.log("KeyguardState", DEBUG, {
-            bool1 = secure
-            bool2 = canDismissLockScreen
-            bool3 = trusted
-            bool4 = trustManaged
-        }, { "#update secure=$bool1 canDismissKeyguard=$bool2" +
-                " trusted=$bool3 trustManaged=$bool4" })
+        logBuffer.log(
+            "KeyguardState",
+            DEBUG,
+            {
+                bool1 = secure
+                bool2 = canDismissLockScreen
+                bool3 = trusted
+                bool4 = trustManaged
+            },
+            {
+                "#update secure=$bool1 canDismissKeyguard=$bool2" +
+                    " trusted=$bool3 trustManaged=$bool4"
+            }
+        )
     }
 
     fun logSkipUpdateFaceListeningOnWakeup(@WakeReason pmWakeReason: Int) {
-        logBuffer.log(TAG, VERBOSE, {
-            str1 = PowerManager.wakeReasonToString(pmWakeReason)
-        }, { "Skip updating face listening state on wakeup from $str1"})
+        logBuffer.log(
+            TAG,
+            VERBOSE,
+            { str1 = PowerManager.wakeReasonToString(pmWakeReason) },
+            { "Skip updating face listening state on wakeup from $str1" }
+        )
     }
 
     fun logTaskStackChangedForAssistant(assistantVisible: Boolean) {
-        logBuffer.log(TAG, VERBOSE, {
-            bool1 = assistantVisible
-        }, {
-            "TaskStackChanged for ACTIVITY_TYPE_ASSISTANT, assistant visible: $bool1"
-        })
+        logBuffer.log(
+            TAG,
+            VERBOSE,
+            { bool1 = assistantVisible },
+            { "TaskStackChanged for ACTIVITY_TYPE_ASSISTANT, assistant visible: $bool1" }
+        )
     }
 
     fun logAssistantVisible(assistantVisible: Boolean) {
-        logBuffer.log(TAG, VERBOSE, {
-            bool1 = assistantVisible
-        }, {
-            "Updating mAssistantVisible to new value: $bool1"
-        })
+        logBuffer.log(
+            TAG,
+            VERBOSE,
+            { bool1 = assistantVisible },
+            { "Updating mAssistantVisible to new value: $bool1" }
+        )
+    }
+
+    fun logFaceEnrolledUpdated(oldValue: Boolean, newValue: Boolean) {
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                bool1 = oldValue
+                bool2 = newValue
+            },
+            { "Face enrolled state changed: old: $bool1, new: $bool2" }
+        )
+    }
+
+    fun logFpEnrolledUpdated(userId: Int, oldValue: Boolean, newValue: Boolean) {
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                int1 = userId
+                bool1 = oldValue
+                bool2 = newValue
+            },
+            { "Fp enrolled state changed for userId: $int1 old: $bool1, new: $bool2" }
+        )
+    }
+
+    fun logTrustUsuallyManagedUpdated(
+        userId: Int,
+        oldValue: Boolean,
+        newValue: Boolean,
+        context: String
+    ) {
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                int1 = userId
+                bool1 = oldValue
+                bool2 = newValue
+                str1 = context
+            },
+            {
+                "trustUsuallyManaged changed for " +
+                    "userId: $int1 " +
+                    "old: $bool1, " +
+                    "new: $bool2 " +
+                    "context: $str1"
+            }
+        )
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index fa4caaf..4a900aa 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -588,8 +588,7 @@
     @JvmField val UDFPS_ELLIPSE_DETECTION = releasedFlag(2201, "udfps_ellipse_detection")
 
     // 2300 - stylus
-    @JvmField
-    val TRACK_STYLUS_EVER_USED = unreleasedFlag(2300, "track_stylus_ever_used", teamfood = true)
+    @JvmField val TRACK_STYLUS_EVER_USED = releasedFlag(2300, "track_stylus_ever_used")
     @JvmField
     val ENABLE_STYLUS_CHARGING_UI =
         unreleasedFlag(2301, "enable_stylus_charging_ui", teamfood = true)
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java
index 12d6b7c..2af0269 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java
@@ -36,6 +36,7 @@
 
 import androidx.core.graphics.drawable.IconCompat;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.settingslib.qrcode.QrCodeGenerator;
 import com.android.systemui.R;
 import com.android.systemui.broadcast.BroadcastSender;
@@ -49,7 +50,7 @@
  */
 @SysUISingleton
 public class MediaOutputBroadcastDialog extends MediaOutputBaseDialog {
-    private static final String TAG = "BroadcastDialog";
+    private static final String TAG = "MediaOutputBroadcastDialog";
 
     private ViewStub mBroadcastInfoArea;
     private ImageView mBroadcastQrCodeView;
@@ -217,7 +218,8 @@
         refreshUi();
     }
 
-    private void refreshUi() {
+    @VisibleForTesting
+    void refreshUi() {
         setQrCodeView();
 
         mCurrentBroadcastName = getBroadcastMetadataInfo(METADATA_BROADCAST_NAME);
@@ -256,7 +258,8 @@
         mIsPasswordHide = !mIsPasswordHide;
     }
 
-    private void launchBroadcastUpdatedDialog(boolean isBroadcastCode, String editString) {
+    @VisibleForTesting
+    void launchBroadcastUpdatedDialog(boolean isBroadcastCode, String editString) {
         final View layout = LayoutInflater.from(mContext).inflate(
                 R.layout.media_output_broadcast_update_dialog, null);
         final EditText editText = layout.requireViewById(R.id.broadcast_edit_text);
@@ -286,7 +289,8 @@
         return mMediaOutputController.getBroadcastMetadata();
     }
 
-    private void updateBroadcastInfo(boolean isBroadcastCode, String updatedString) {
+    @VisibleForTesting
+    void updateBroadcastInfo(boolean isBroadcastCode, String updatedString) {
         Button positiveBtn = mAlertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
         if (positiveBtn != null) {
             positiveBtn.setEnabled(false);
@@ -377,16 +381,35 @@
     }
 
     private void handleUpdateFailedUi() {
-        final Button positiveBtn = mAlertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
-        mBroadcastErrorMessage.setVisibility(View.VISIBLE);
+        if (mAlertDialog == null) {
+            Log.d(TAG, "handleUpdateFailedUi: mAlertDialog is null");
+            return;
+        }
+        int errorMessageStringId = -1;
+        boolean enablePositiveBtn = false;
         if (mRetryCount < MAX_BROADCAST_INFO_UPDATE) {
-            if (positiveBtn != null) {
-                positiveBtn.setEnabled(true);
-            }
-            mBroadcastErrorMessage.setText(R.string.media_output_broadcast_update_error);
+            enablePositiveBtn = true;
+            errorMessageStringId = R.string.media_output_broadcast_update_error;
         } else {
             mRetryCount = 0;
-            mBroadcastErrorMessage.setText(R.string.media_output_broadcast_last_update_error);
+            errorMessageStringId = R.string.media_output_broadcast_last_update_error;
         }
+
+        // update UI
+        final Button positiveBtn = mAlertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
+        if (positiveBtn != null && enablePositiveBtn) {
+            positiveBtn.setEnabled(true);
+        }
+        if (mBroadcastErrorMessage != null) {
+            mBroadcastErrorMessage.setVisibility(View.VISIBLE);
+            if (errorMessageStringId > 0) {
+                mBroadcastErrorMessage.setText(errorMessageStringId);
+            }
+        }
+    }
+
+    @VisibleForTesting
+    int getRetryCount() {
+        return mRetryCount;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java
index f712629..2b6327f 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.shade;
 
-import static android.os.Trace.TRACE_TAG_ALWAYS;
+import static android.os.Trace.TRACE_TAG_APP;
 import static android.view.WindowInsets.Type.systemBars;
 
 import static com.android.systemui.statusbar.phone.CentralSurfaces.DEBUG;
@@ -328,7 +328,7 @@
 
     @Override
     public void requestLayout() {
-        Trace.instant(TRACE_TAG_ALWAYS, "NotificationShadeWindowView#requestLayout");
+        Trace.instant(TRACE_TAG_APP, "NotificationShadeWindowView#requestLayout");
         super.requestLayout();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index 32b8e09..c9f31ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -166,6 +166,11 @@
 
 
     private Queue<NotifEvent> mEventQueue = new ArrayDeque<>();
+    private final Runnable mRebuildListRunnable = () -> {
+        if (mBuildListener != null) {
+            mBuildListener.onBuildList(mReadOnlyNotificationSet, "asynchronousUpdate");
+        }
+    };
 
     private boolean mAttached = false;
     private boolean mAmDispatchingToOtherCode;
@@ -462,7 +467,7 @@
             int modificationType) {
         Assert.isMainThread();
         mEventQueue.add(new ChannelChangedEvent(pkgName, user, channel, modificationType));
-        dispatchEventsAndRebuildList("onNotificationChannelModified");
+        dispatchEventsAndAsynchronouslyRebuildList();
     }
 
     private void onNotificationsInitialized() {
@@ -621,15 +626,39 @@
 
     private void dispatchEventsAndRebuildList(String reason) {
         Trace.beginSection("NotifCollection.dispatchEventsAndRebuildList");
+        if (mMainHandler.hasCallbacks(mRebuildListRunnable)) {
+            mMainHandler.removeCallbacks(mRebuildListRunnable);
+        }
+
+        dispatchEvents();
+
+        if (mBuildListener != null) {
+            mBuildListener.onBuildList(mReadOnlyNotificationSet, reason);
+        }
+        Trace.endSection();
+    }
+
+    private void dispatchEventsAndAsynchronouslyRebuildList() {
+        Trace.beginSection("NotifCollection.dispatchEventsAndAsynchronouslyRebuildList");
+
+        dispatchEvents();
+
+        if (!mMainHandler.hasCallbacks(mRebuildListRunnable)) {
+            mMainHandler.postDelayed(mRebuildListRunnable, 1000L);
+        }
+
+        Trace.endSection();
+    }
+
+    private void dispatchEvents() {
+        Trace.beginSection("NotifCollection.dispatchEvents");
+
         mAmDispatchingToOtherCode = true;
         while (!mEventQueue.isEmpty()) {
             mEventQueue.remove().dispatchTo(mNotifCollectionListeners);
         }
         mAmDispatchingToOtherCode = false;
 
-        if (mBuildListener != null) {
-            mBuildListener.onBuildList(mReadOnlyNotificationSet, reason);
-        }
         Trace.endSection();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 1fb7eb5..d2087ba6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.statusbar.notification.stack;
 
-import static android.os.Trace.TRACE_TAG_ALWAYS;
+import static android.os.Trace.TRACE_TAG_APP;
 
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING;
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_SHADE_CLEAR_ALL;
@@ -1121,7 +1121,7 @@
 
     @Override
     public void requestLayout() {
-        Trace.instant(TRACE_TAG_ALWAYS, "NotificationStackScrollLayout#requestLayout");
+        Trace.instant(TRACE_TAG_APP, "NotificationStackScrollLayout#requestLayout");
         super.requestLayout();
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java
new file mode 100644
index 0000000..87f74b5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2023 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.systemui.media.dialog;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.app.KeyguardManager;
+import android.media.AudioManager;
+import android.media.session.MediaController;
+import android.media.session.MediaSessionManager;
+import android.media.session.PlaybackState;
+import android.os.PowerExemptionManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastMetadata;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+import com.android.settingslib.media.LocalMediaManager;
+import com.android.settingslib.media.MediaDevice;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.DialogLaunchAnimator;
+import com.android.systemui.broadcast.BroadcastSender;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
+import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class MediaOutputBroadcastDialogTest extends SysuiTestCase {
+
+    private static final String TEST_PACKAGE = "test_package";
+    private static final String BROADCAST_NAME_TEST = "Broadcast_name_test";
+    private static final String BROADCAST_CODE_TEST = "112233";
+    private static final String BROADCAST_CODE_UPDATE_TEST = "11223344";
+
+    // Mock
+    private final MediaSessionManager mMediaSessionManager = mock(MediaSessionManager.class);
+    private MediaController mMediaController = mock(MediaController.class);
+    private PlaybackState mPlaybackState = mock(PlaybackState.class);
+    private final LocalBluetoothManager mLocalBluetoothManager = mock(LocalBluetoothManager.class);
+    private final LocalBluetoothProfileManager mLocalBluetoothProfileManager = mock(
+            LocalBluetoothProfileManager.class);
+    private final LocalBluetoothLeBroadcast mLocalBluetoothLeBroadcast = mock(
+            LocalBluetoothLeBroadcast.class);
+    private final ActivityStarter mStarter = mock(ActivityStarter.class);
+    private final BroadcastSender mBroadcastSender = mock(BroadcastSender.class);
+    private final LocalMediaManager mLocalMediaManager = mock(LocalMediaManager.class);
+    private final MediaDevice mMediaDevice = mock(MediaDevice.class);
+    private final CommonNotifCollection mNotifCollection = mock(CommonNotifCollection.class);
+    private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
+    private final NearbyMediaDevicesManager mNearbyMediaDevicesManager = mock(
+            NearbyMediaDevicesManager.class);
+    private final AudioManager mAudioManager = mock(AudioManager.class);
+    private PowerExemptionManager mPowerExemptionManager = mock(PowerExemptionManager.class);
+    private KeyguardManager mKeyguardManager = mock(KeyguardManager.class);
+    private final LocalBluetoothLeBroadcastMetadata mLocalBluetoothLeBroadcastMetadata =
+            mock(LocalBluetoothLeBroadcastMetadata.class);
+    private FeatureFlags mFlags = mock(FeatureFlags.class);
+
+    private List<MediaController> mMediaControllers = new ArrayList<>();
+    private MediaOutputBroadcastDialog mMediaOutputBroadcastDialog;
+    private MediaOutputController mMediaOutputController;
+    private final List<String> mFeatures = new ArrayList<>();
+
+    @Before
+    public void setUp() {
+        when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager);
+        when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile())
+                .thenReturn(mLocalBluetoothLeBroadcast);
+        when(mMediaController.getPlaybackState()).thenReturn(mPlaybackState);
+        when(mPlaybackState.getState()).thenReturn(PlaybackState.STATE_NONE);
+        when(mMediaController.getPackageName()).thenReturn(TEST_PACKAGE);
+        mMediaControllers.add(mMediaController);
+        when(mMediaSessionManager.getActiveSessions(any())).thenReturn(mMediaControllers);
+        when(mLocalBluetoothLeBroadcast.getLocalBluetoothLeBroadcastMetaData()).thenReturn(
+                mLocalBluetoothLeBroadcastMetadata);
+        when(mLocalBluetoothLeBroadcastMetadata.convertToQrCodeString())
+                .thenReturn("metadata_test_convert");
+        when(mLocalBluetoothLeBroadcast.getProgramInfo()).thenReturn(BROADCAST_NAME_TEST);
+        when(mLocalBluetoothLeBroadcast.getBroadcastCode())
+                .thenReturn(BROADCAST_CODE_TEST.getBytes(StandardCharsets.UTF_8));
+
+        mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE,
+                mMediaSessionManager, mLocalBluetoothManager, mStarter,
+                mNotifCollection, mDialogLaunchAnimator,
+                Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager,
+                mKeyguardManager, mFlags);
+        when(mFlags.isEnabled(Flags.OUTPUT_SWITCHER_ADVANCED_LAYOUT)).thenReturn(false);
+
+        mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
+        mMediaOutputBroadcastDialog = new MediaOutputBroadcastDialog(mContext, false,
+                mBroadcastSender, mMediaOutputController);
+        mMediaOutputBroadcastDialog.show();
+
+        when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mMediaDevice);
+        when(mMediaDevice.getFeatures()).thenReturn(mFeatures);
+    }
+
+    @After
+    public void tearDown() {
+        mMediaOutputBroadcastDialog.dismissDialog();
+    }
+
+    @Test
+    public void refreshUi_checkBroadcastQrCodeView() {
+        mMediaOutputBroadcastDialog.refreshUi();
+        final ImageView broadcastQrCodeView = mMediaOutputBroadcastDialog.mDialogView
+                .requireViewById(R.id.qrcode_view);
+
+        assertThat(broadcastQrCodeView.getDrawable()).isNotNull();
+    }
+
+    @Test
+    public void refreshUi_checkBroadcastName() {
+        mMediaOutputBroadcastDialog.refreshUi();
+        final TextView broadcastName = mMediaOutputBroadcastDialog.mDialogView
+                .requireViewById(R.id.broadcast_name_summary);
+
+        assertThat(broadcastName.getText().toString()).isEqualTo(BROADCAST_NAME_TEST);
+    }
+
+    @Test
+    public void refreshUi_checkBroadcastCode() {
+        mMediaOutputBroadcastDialog.refreshUi();
+        final TextView broadcastCode = mMediaOutputBroadcastDialog.mDialogView
+                .requireViewById(R.id.broadcast_code_summary);
+
+        assertThat(broadcastCode.getText().toString()).isEqualTo(BROADCAST_CODE_TEST);
+    }
+
+    @Test
+    public void updateBroadcastInfo_stopBroadcastFailed_handleFailedUI() {
+        mMediaOutputBroadcastDialog.launchBroadcastUpdatedDialog(true, BROADCAST_CODE_UPDATE_TEST);
+        when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile())
+                .thenReturn(null);
+
+        mMediaOutputBroadcastDialog.updateBroadcastInfo(true, BROADCAST_CODE_UPDATE_TEST);
+        assertThat(mMediaOutputBroadcastDialog.getRetryCount()).isEqualTo(1);
+
+        mMediaOutputBroadcastDialog.updateBroadcastInfo(true, BROADCAST_CODE_UPDATE_TEST);
+        assertThat(mMediaOutputBroadcastDialog.getRetryCount()).isEqualTo(2);
+
+        // It will be the MAX Retry Count = 3
+        mMediaOutputBroadcastDialog.updateBroadcastInfo(true, BROADCAST_CODE_UPDATE_TEST);
+        assertThat(mMediaOutputBroadcastDialog.getRetryCount()).isEqualTo(0);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
index 005c80a..540bda6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
@@ -106,6 +106,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.mockito.Spy;
+import org.mockito.stubbing.Answer;
 
 import java.util.Arrays;
 import java.util.Collection;
@@ -378,6 +379,90 @@
     }
 
     @Test
+    public void testScheduleBuildNotificationListWhenChannelChanged() {
+        // GIVEN
+        final NotificationEntryBuilder neb = buildNotif(TEST_PACKAGE, 48);
+        final NotificationChannel channel = new NotificationChannel(
+                "channelId",
+                "channelName",
+                NotificationManager.IMPORTANCE_DEFAULT);
+        neb.setChannel(channel);
+
+        final NotifEvent notif = mNoMan.postNotif(neb);
+        final NotificationEntry entry = mCollectionListener.getEntry(notif.key);
+
+        when(mMainHandler.hasCallbacks(any())).thenReturn(false);
+
+        clearInvocations(mBuildListener);
+
+        // WHEN
+        mNotifHandler.onNotificationChannelModified(TEST_PACKAGE,
+                entry.getSbn().getUser(), channel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
+
+        // THEN
+        verify(mMainHandler).postDelayed(any(), eq(1000L));
+    }
+
+    @Test
+    public void testCancelScheduledBuildNotificationListEventWhenNotifUpdatedSynchronously() {
+        // GIVEN
+        final NotificationEntry entry1 = buildNotif(TEST_PACKAGE, 1)
+                .setGroup(mContext, "group_1")
+                .build();
+        final NotificationEntry entry2 = buildNotif(TEST_PACKAGE, 2)
+                .setGroup(mContext, "group_1")
+                .setContentTitle(mContext, "New version")
+                .build();
+        final NotificationEntry entry3 = buildNotif(TEST_PACKAGE, 3)
+                .setGroup(mContext, "group_1")
+                .build();
+
+        final List<CoalescedEvent> entriesToBePosted = Arrays.asList(
+                new CoalescedEvent(entry1.getKey(), 0, entry1.getSbn(), entry1.getRanking(), null),
+                new CoalescedEvent(entry2.getKey(), 1, entry2.getSbn(), entry2.getRanking(), null),
+                new CoalescedEvent(entry3.getKey(), 2, entry3.getSbn(), entry3.getRanking(), null)
+        );
+
+        when(mMainHandler.hasCallbacks(any())).thenReturn(true);
+
+        // WHEN
+        mNotifHandler.onNotificationBatchPosted(entriesToBePosted);
+
+        // THEN
+        verify(mMainHandler).removeCallbacks(any());
+    }
+
+    @Test
+    public void testBuildNotificationListWhenChannelChanged() {
+        // GIVEN
+        final NotificationEntryBuilder neb = buildNotif(TEST_PACKAGE, 48);
+        final NotificationChannel channel = new NotificationChannel(
+                "channelId",
+                "channelName",
+                NotificationManager.IMPORTANCE_DEFAULT);
+        neb.setChannel(channel);
+
+        final NotifEvent notif = mNoMan.postNotif(neb);
+        final NotificationEntry entry = mCollectionListener.getEntry(notif.key);
+
+        when(mMainHandler.hasCallbacks(any())).thenReturn(false);
+        when(mMainHandler.postDelayed(any(), eq(1000L))).thenAnswer((Answer) invocation -> {
+            final Runnable runnable = invocation.getArgument(0);
+            runnable.run();
+            return null;
+        });
+
+        clearInvocations(mBuildListener);
+
+        // WHEN
+        mNotifHandler.onNotificationChannelModified(TEST_PACKAGE,
+                entry.getSbn().getUser(), channel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
+
+        // THEN
+        verifyBuiltList(List.of(entry));
+    }
+
+    @Test
     public void testRankingsAreUpdatedForOtherNotifs() {
         // GIVEN a collection with one notif
         NotifEvent notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 3)
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 328b971..cde820a 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -4577,6 +4577,17 @@
         return false;
     }
 
+    /**
+     * Called when always on magnification feature flag flips to check if the feature should be
+     * enabled for current user state.
+     */
+    public void updateAlwaysOnMagnification() {
+        synchronized (mLock) {
+            readAlwaysOnMagnificationLocked(getCurrentUserState());
+        }
+    }
+
+    @GuardedBy("mLock")
     boolean readAlwaysOnMagnificationLocked(AccessibilityUserState userState) {
         final boolean isSettingsAlwaysOnEnabled = Settings.Secure.getIntForUser(
                 mContext.getContentResolver(),
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/AlwaysOnMagnificationFeatureFlag.java b/services/accessibility/java/com/android/server/accessibility/magnification/AlwaysOnMagnificationFeatureFlag.java
index ed45e7b..16d2e6b 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/AlwaysOnMagnificationFeatureFlag.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/AlwaysOnMagnificationFeatureFlag.java
@@ -16,10 +16,13 @@
 
 package com.android.server.accessibility.magnification;
 
+import android.annotation.NonNull;
 import android.provider.DeviceConfig;
 
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.util.concurrent.Executor;
+
 /**
  * Encapsulates the feature flags for always on magnification. {@see DeviceConfig}
  *
@@ -50,4 +53,39 @@
                 Boolean.toString(isEnabled),
                 /* makeDefault= */ false);
     }
+
+    /**
+     * Adds a listener for when the feature flag changes.
+     *
+     * <p>{@see DeviceConfig#addOnPropertiesChangedListener(
+     * String, Executor, DeviceConfig.OnPropertiesChangedListener)}
+     */
+    @NonNull
+    public static DeviceConfig.OnPropertiesChangedListener addOnChangedListener(
+            @NonNull Executor executor, @NonNull Runnable listener) {
+        DeviceConfig.OnPropertiesChangedListener onChangedListener =
+                properties -> {
+                    if (properties.getKeyset().contains(
+                            FEATURE_NAME_ENABLE_ALWAYS_ON_MAGNIFICATION)) {
+                        listener.run();
+                    }
+                };
+        DeviceConfig.addOnPropertiesChangedListener(
+                NAMESPACE,
+                executor,
+                onChangedListener);
+
+        return onChangedListener;
+    }
+
+    /**
+     * Remove a listener for when the feature flag changes.
+     *
+     * <p>{@see DeviceConfig#addOnPropertiesChangedListener(String, Executor,
+     * DeviceConfig.OnPropertiesChangedListener)}
+     */
+    public static void removeOnChangedListener(
+            @NonNull DeviceConfig.OnPropertiesChangedListener onChangedListener) {
+        DeviceConfig.removeOnPropertiesChangedListener(onChangedListener);
+    }
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index a6e6bd7..4753a54 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -48,6 +48,7 @@
 import com.android.internal.accessibility.util.AccessibilityStatsLogUtils;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ConcurrentUtils;
 import com.android.server.LocalServices;
 import com.android.server.accessibility.AccessibilityManagerService;
 import com.android.server.wm.WindowManagerInternal;
@@ -149,6 +150,9 @@
                 .getAccessibilityController().setUiChangesForAccessibilityCallbacks(this);
         mSupportWindowMagnification = context.getPackageManager().hasSystemFeature(
                 FEATURE_WINDOW_MAGNIFICATION);
+
+        AlwaysOnMagnificationFeatureFlag.addOnChangedListener(
+                ConcurrentUtils.DIRECT_EXECUTOR, mAms::updateAlwaysOnMagnification);
     }
 
     @VisibleForTesting
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 df5e37c..21b51b1 100644
--- a/services/companion/java/com/android/server/companion/virtual/InputController.java
+++ b/services/companion/java/com/android/server/companion/virtual/InputController.java
@@ -242,7 +242,7 @@
     private void closeInputDeviceDescriptorLocked(IBinder token,
             InputDeviceDescriptor inputDeviceDescriptor) {
         token.unlinkToDeath(inputDeviceDescriptor.getDeathRecipient(), /* flags= */ 0);
-        mNativeWrapper.closeUinput(inputDeviceDescriptor.getFileDescriptor());
+        mNativeWrapper.closeUinput(inputDeviceDescriptor.getNativePointer());
         String phys = inputDeviceDescriptor.getPhys();
         InputManager.getInstance().removeUniqueIdAssociation(phys);
         // Type associations are added in the case of navigation touchpads. Those should be removed
@@ -366,7 +366,7 @@
                 throw new IllegalArgumentException(
                         "Could not send key event to input device for given token");
             }
-            return mNativeWrapper.writeDpadKeyEvent(inputDeviceDescriptor.getFileDescriptor(),
+            return mNativeWrapper.writeDpadKeyEvent(inputDeviceDescriptor.getNativePointer(),
                     event.getKeyCode(), event.getAction());
         }
     }
@@ -379,7 +379,7 @@
                 throw new IllegalArgumentException(
                         "Could not send key event to input device for given token");
             }
-            return mNativeWrapper.writeKeyEvent(inputDeviceDescriptor.getFileDescriptor(),
+            return mNativeWrapper.writeKeyEvent(inputDeviceDescriptor.getNativePointer(),
                     event.getKeyCode(), event.getAction());
         }
     }
@@ -397,7 +397,7 @@
                 throw new IllegalStateException(
                         "Display id associated with this mouse is not currently targetable");
             }
-            return mNativeWrapper.writeButtonEvent(inputDeviceDescriptor.getFileDescriptor(),
+            return mNativeWrapper.writeButtonEvent(inputDeviceDescriptor.getNativePointer(),
                     event.getButtonCode(), event.getAction());
         }
     }
@@ -410,7 +410,7 @@
                 throw new IllegalArgumentException(
                         "Could not send touch event to input device for given token");
             }
-            return mNativeWrapper.writeTouchEvent(inputDeviceDescriptor.getFileDescriptor(),
+            return mNativeWrapper.writeTouchEvent(inputDeviceDescriptor.getNativePointer(),
                     event.getPointerId(), event.getToolType(), event.getAction(), event.getX(),
                     event.getY(), event.getPressure(), event.getMajorAxisSize());
         }
@@ -429,7 +429,7 @@
                 throw new IllegalStateException(
                         "Display id associated with this mouse is not currently targetable");
             }
-            return mNativeWrapper.writeRelativeEvent(inputDeviceDescriptor.getFileDescriptor(),
+            return mNativeWrapper.writeRelativeEvent(inputDeviceDescriptor.getNativePointer(),
                     event.getRelativeX(), event.getRelativeY());
         }
     }
@@ -447,7 +447,7 @@
                 throw new IllegalStateException(
                         "Display id associated with this mouse is not currently targetable");
             }
-            return mNativeWrapper.writeScrollEvent(inputDeviceDescriptor.getFileDescriptor(),
+            return mNativeWrapper.writeScrollEvent(inputDeviceDescriptor.getNativePointer(),
                     event.getXAxisMovement(), event.getYAxisMovement());
         }
     }
@@ -474,7 +474,7 @@
         synchronized (mLock) {
             fout.println("      Active descriptors: ");
             for (InputDeviceDescriptor inputDeviceDescriptor : mInputDeviceDescriptors.values()) {
-                fout.println("        fd: " + inputDeviceDescriptor.getFileDescriptor());
+                fout.println("        ptr: " + inputDeviceDescriptor.getNativePointer());
                 fout.println("          displayId: " + inputDeviceDescriptor.getDisplayId());
                 fout.println("          creationOrder: "
                         + inputDeviceDescriptor.getCreationOrderNumber());
@@ -487,10 +487,10 @@
     }
 
     @VisibleForTesting
-    void addDeviceForTesting(IBinder deviceToken, int fd, int type, int displayId, String phys,
+    void addDeviceForTesting(IBinder deviceToken, long ptr, int type, int displayId, String phys,
             String deviceName, int inputDeviceId) {
         synchronized (mLock) {
-            mInputDeviceDescriptors.put(deviceToken, new InputDeviceDescriptor(fd, () -> {
+            mInputDeviceDescriptors.put(deviceToken, new InputDeviceDescriptor(ptr, () -> {
             }, type, displayId, phys, deviceName, inputDeviceId));
         }
     }
@@ -504,75 +504,76 @@
         return inputDeviceDescriptors;
     }
 
-    private static native int nativeOpenUinputDpad(String deviceName, int vendorId, int productId,
+    private static native long nativeOpenUinputDpad(String deviceName, int vendorId, int productId,
             String phys);
-    private static native int nativeOpenUinputKeyboard(String deviceName, int vendorId,
+    private static native long nativeOpenUinputKeyboard(String deviceName, int vendorId,
             int productId, String phys);
-    private static native int nativeOpenUinputMouse(String deviceName, int vendorId, int productId,
+    private static native long nativeOpenUinputMouse(String deviceName, int vendorId, int productId,
             String phys);
-    private static native int nativeOpenUinputTouchscreen(String deviceName, int vendorId,
+    private static native long nativeOpenUinputTouchscreen(String deviceName, int vendorId,
             int productId, String phys, int height, int width);
-    private static native boolean nativeCloseUinput(int fd);
-    private static native boolean nativeWriteDpadKeyEvent(int fd, int androidKeyCode, int action);
-    private static native boolean nativeWriteKeyEvent(int fd, int androidKeyCode, int action);
-    private static native boolean nativeWriteButtonEvent(int fd, int buttonCode, int action);
-    private static native boolean nativeWriteTouchEvent(int fd, int pointerId, int toolType,
+    private static native void nativeCloseUinput(long ptr);
+    private static native boolean nativeWriteDpadKeyEvent(long ptr, int androidKeyCode, int action);
+    private static native boolean nativeWriteKeyEvent(long ptr, int androidKeyCode, int action);
+    private static native boolean nativeWriteButtonEvent(long ptr, int buttonCode, int action);
+    private static native boolean nativeWriteTouchEvent(long ptr, int pointerId, int toolType,
             int action, float locationX, float locationY, float pressure, float majorAxisSize);
-    private static native boolean nativeWriteRelativeEvent(int fd, float relativeX,
+    private static native boolean nativeWriteRelativeEvent(long ptr, float relativeX,
             float relativeY);
-    private static native boolean nativeWriteScrollEvent(int fd, float xAxisMovement,
+    private static native boolean nativeWriteScrollEvent(long ptr, float xAxisMovement,
             float yAxisMovement);
 
     /** Wrapper around the static native methods for tests. */
     @VisibleForTesting
     protected static class NativeWrapper {
-        public int openUinputDpad(String deviceName, int vendorId, int productId, String phys) {
+        public long openUinputDpad(String deviceName, int vendorId, int productId, String phys) {
             return nativeOpenUinputDpad(deviceName, vendorId, productId, phys);
         }
 
-        public int openUinputKeyboard(String deviceName, int vendorId, int productId, String phys) {
+        public long openUinputKeyboard(String deviceName, int vendorId, int productId,
+                String phys) {
             return nativeOpenUinputKeyboard(deviceName, vendorId, productId, phys);
         }
 
-        public int openUinputMouse(String deviceName, int vendorId, int productId, String phys) {
+        public long openUinputMouse(String deviceName, int vendorId, int productId, String phys) {
             return nativeOpenUinputMouse(deviceName, vendorId, productId, phys);
         }
 
-        public int openUinputTouchscreen(String deviceName, int vendorId,
+        public long openUinputTouchscreen(String deviceName, int vendorId,
                 int productId, String phys, int height, int width) {
             return nativeOpenUinputTouchscreen(deviceName, vendorId, productId, phys, height,
                     width);
         }
 
-        public boolean closeUinput(int fd) {
-            return nativeCloseUinput(fd);
+        public void closeUinput(long ptr) {
+            nativeCloseUinput(ptr);
         }
 
-        public boolean writeDpadKeyEvent(int fd, int androidKeyCode, int action) {
-            return nativeWriteDpadKeyEvent(fd, androidKeyCode, action);
+        public boolean writeDpadKeyEvent(long ptr, int androidKeyCode, int action) {
+            return nativeWriteDpadKeyEvent(ptr, androidKeyCode, action);
         }
 
-        public boolean writeKeyEvent(int fd, int androidKeyCode, int action) {
-            return nativeWriteKeyEvent(fd, androidKeyCode, action);
+        public boolean writeKeyEvent(long ptr, int androidKeyCode, int action) {
+            return nativeWriteKeyEvent(ptr, androidKeyCode, action);
         }
 
-        public boolean writeButtonEvent(int fd, int buttonCode, int action) {
-            return nativeWriteButtonEvent(fd, buttonCode, action);
+        public boolean writeButtonEvent(long ptr, int buttonCode, int action) {
+            return nativeWriteButtonEvent(ptr, buttonCode, action);
         }
 
-        public boolean writeTouchEvent(int fd, int pointerId, int toolType, int action,
+        public boolean writeTouchEvent(long ptr, int pointerId, int toolType, int action,
                 float locationX, float locationY, float pressure, float majorAxisSize) {
-            return nativeWriteTouchEvent(fd, pointerId, toolType,
+            return nativeWriteTouchEvent(ptr, pointerId, toolType,
                     action, locationX, locationY,
                     pressure, majorAxisSize);
         }
 
-        public boolean writeRelativeEvent(int fd, float relativeX, float relativeY) {
-            return nativeWriteRelativeEvent(fd, relativeX, relativeY);
+        public boolean writeRelativeEvent(long ptr, float relativeX, float relativeY) {
+            return nativeWriteRelativeEvent(ptr, relativeX, relativeY);
         }
 
-        public boolean writeScrollEvent(int fd, float xAxisMovement, float yAxisMovement) {
-            return nativeWriteScrollEvent(fd, xAxisMovement,
+        public boolean writeScrollEvent(long ptr, float xAxisMovement, float yAxisMovement) {
+            return nativeWriteScrollEvent(ptr, xAxisMovement,
                     yAxisMovement);
         }
     }
@@ -597,7 +598,8 @@
 
         private static final AtomicLong sNextCreationOrderNumber = new AtomicLong(1);
 
-        private final int mFd;
+        // Pointer to the native input device object.
+        private final long mPtr;
         private final IBinder.DeathRecipient mDeathRecipient;
         private final @Type int mType;
         private final int mDisplayId;
@@ -611,9 +613,9 @@
         // Monotonically increasing number; devices with lower numbers were created earlier.
         private final long mCreationOrderNumber;
 
-        InputDeviceDescriptor(int fd, IBinder.DeathRecipient deathRecipient, @Type int type,
+        InputDeviceDescriptor(long ptr, IBinder.DeathRecipient deathRecipient, @Type int type,
                 int displayId, String phys, String name, int inputDeviceId) {
-            mFd = fd;
+            mPtr = ptr;
             mDeathRecipient = deathRecipient;
             mType = type;
             mDisplayId = displayId;
@@ -623,8 +625,8 @@
             mCreationOrderNumber = sNextCreationOrderNumber.getAndIncrement();
         }
 
-        public int getFileDescriptor() {
-            return mFd;
+        public long getNativePointer() {
+            return mPtr;
         }
 
         public int getType() {
@@ -767,7 +769,7 @@
      */
     private void createDeviceInternal(@InputDeviceDescriptor.Type int type, String deviceName,
             int vendorId, int productId, IBinder deviceToken, int displayId, String phys,
-            Supplier<Integer> deviceOpener)
+            Supplier<Long> deviceOpener)
             throws DeviceCreationException {
         if (!mThreadVerifier.isValidThread()) {
             throw new IllegalStateException(
@@ -776,19 +778,22 @@
         }
         validateDeviceName(deviceName);
 
-        final int fd;
+        final long ptr;
         final BinderDeathRecipient binderDeathRecipient;
 
         final int inputDeviceId;
 
         setUniqueIdAssociation(displayId, phys);
         try (WaitForDevice waiter = new WaitForDevice(deviceName, vendorId, productId)) {
-            fd = deviceOpener.get();
-            if (fd < 0) {
+            ptr = deviceOpener.get();
+            // See INVALID_PTR in libs/input/VirtualInputDevice.cpp.
+            if (ptr == 0) {
                 throw new DeviceCreationException(
-                        "A native error occurred when creating touchscreen: " + -fd);
+                        "A native error occurred when creating virtual input device: "
+                                + deviceName);
             }
-            // The fd is valid from here, so ensure that all failures close the fd after this point.
+            // The pointer to the native input device is valid from here, so ensure that all
+            // failures close the device after this point.
             try {
                 inputDeviceId = waiter.waitForDeviceCreation();
 
@@ -800,7 +805,7 @@
                             "Client died before virtual device could be created.", e);
                 }
             } catch (DeviceCreationException e) {
-                mNativeWrapper.closeUinput(fd);
+                mNativeWrapper.closeUinput(ptr);
                 throw e;
             }
         } catch (DeviceCreationException e) {
@@ -810,7 +815,7 @@
 
         synchronized (mLock) {
             mInputDeviceDescriptors.put(deviceToken,
-                    new InputDeviceDescriptor(fd, binderDeathRecipient, type, displayId, phys,
+                    new InputDeviceDescriptor(ptr, binderDeathRecipient, type, displayId, phys,
                             deviceName, inputDeviceId));
         }
     }
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 44f475f..9e95e5f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -129,6 +129,7 @@
     static final String KEY_KILL_BG_RESTRICTED_CACHED_IDLE = "kill_bg_restricted_cached_idle";
     static final String KEY_KILL_BG_RESTRICTED_CACHED_IDLE_SETTLE_TIME =
             "kill_bg_restricted_cached_idle_settle_time";
+    static final String KEY_MAX_PREVIOUS_TIME = "max_previous_time";
     /**
      * Note this key is on {@link DeviceConfig#NAMESPACE_ACTIVITY_MANAGER_COMPONENT_ALIAS}.
      * @see #mEnableComponentAlias
@@ -145,6 +146,9 @@
      */
     static final String KEY_NETWORK_ACCESS_TIMEOUT_MS = "network_access_timeout_ms";
 
+    static final String KEY_USE_TIERED_CACHED_ADJ = "use_tiered_cached_adj";
+    static final String KEY_TIERED_CACHED_ADJ_DECAY_TIME = "tiered_cached_adj_decay_time";
+
     private static final int DEFAULT_MAX_CACHED_PROCESSES = 32;
     private static final boolean DEFAULT_PRIORITIZE_ALARM_BROADCASTS = true;
     private static final long DEFAULT_FGSERVICE_MIN_SHOWN_TIME = 2*1000;
@@ -192,6 +196,7 @@
     private static final float DEFAULT_FGS_START_DENIED_LOG_SAMPLE_RATE = 1; // 100%
     private static final long DEFAULT_PROCESS_KILL_TIMEOUT_MS = 10 * 1000;
     private static final long DEFAULT_NETWORK_ACCESS_TIMEOUT_MS = 200; // 0.2 sec
+    private static final long DEFAULT_MAX_PREVIOUS_TIME = 60 * 1000; // 60s
 
     static final long DEFAULT_BACKGROUND_SETTLE_TIME = 60 * 1000;
     static final long DEFAULT_KILL_BG_RESTRICTED_CACHED_IDLE_SETTLE_TIME_MS = 60 * 1000;
@@ -199,6 +204,9 @@
 
     static final int DEFAULT_MAX_SERVICE_CONNECTIONS_PER_PROCESS = 3000;
 
+    private static final boolean DEFAULT_USE_TIERED_CACHED_ADJ = false;
+    private static final long DEFAULT_TIERED_CACHED_ADJ_DECAY_TIME = 60 * 1000;
+
     /**
      * Same as {@link TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED}
      */
@@ -534,6 +542,9 @@
     public long TOP_TO_ALMOST_PERCEPTIBLE_GRACE_DURATION =
             DEFAULT_TOP_TO_ALMOST_PERCEPTIBLE_GRACE_DURATION;
 
+    // How long a process can remain at previous oom_adj before dropping to cached
+    public static long MAX_PREVIOUS_TIME = DEFAULT_MAX_PREVIOUS_TIME;
+
     /**
      * The minimum time we allow between crashes, for us to consider this
      * application to be bad and stop its services and reject broadcasts.
@@ -1006,6 +1017,12 @@
     public volatile long mShortFgsAnrExtraWaitDuration =
             DEFAULT_SHORT_FGS_ANR_EXTRA_WAIT_DURATION;
 
+    /** @see #KEY_USE_TIERED_CACHED_ADJ */
+    public boolean USE_TIERED_CACHED_ADJ = DEFAULT_USE_TIERED_CACHED_ADJ;
+
+    /** @see #KEY_TIERED_CACHED_ADJ_DECAY_TIME */
+    public long TIERED_CACHED_ADJ_DECAY_TIME = DEFAULT_TIERED_CACHED_ADJ_DECAY_TIME;
+
     private final OnPropertiesChangedListener mOnDeviceConfigChangedListener =
             new OnPropertiesChangedListener() {
                 @Override
@@ -1171,6 +1188,13 @@
                             case KEY_ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION:
                                 updateEnableWaitForFinishAttachApplication();
                                 break;
+                            case KEY_MAX_PREVIOUS_TIME:
+                                updateMaxPreviousTime();
+                                break;
+                            case KEY_USE_TIERED_CACHED_ADJ:
+                            case KEY_TIERED_CACHED_ADJ_DECAY_TIME:
+                                updateUseTieredCachedAdj();
+                                break;
                             default:
                                 break;
                         }
@@ -1825,6 +1849,7 @@
                 DEFAULT_LOW_SWAP_THRESHOLD_PERCENT);
     }
 
+
     private void updateTopToFgsGraceDuration() {
         TOP_TO_FGS_GRACE_DURATION = DeviceConfig.getLong(
                 DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -1832,6 +1857,13 @@
                 DEFAULT_TOP_TO_FGS_GRACE_DURATION);
     }
 
+    private void updateMaxPreviousTime() {
+        MAX_PREVIOUS_TIME = DeviceConfig.getLong(
+                DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                KEY_MAX_PREVIOUS_TIME,
+                DEFAULT_MAX_PREVIOUS_TIME);
+    }
+
     private void updateMinAssocLogDuration() {
         MIN_ASSOC_LOG_DURATION = DeviceConfig.getLong(
                 DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_MIN_ASSOC_LOG_DURATION,
@@ -1908,6 +1940,17 @@
                 DEFAULT_ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION);
     }
 
+    private void updateUseTieredCachedAdj() {
+        USE_TIERED_CACHED_ADJ = DeviceConfig.getBoolean(
+            DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+            KEY_USE_TIERED_CACHED_ADJ,
+            DEFAULT_USE_TIERED_CACHED_ADJ);
+        TIERED_CACHED_ADJ_DECAY_TIME = DeviceConfig.getLong(
+            DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+            KEY_TIERED_CACHED_ADJ_DECAY_TIME,
+            DEFAULT_TIERED_CACHED_ADJ_DECAY_TIME);
+    }
+
     @NeverCompile // Avoid size overhead of debugging code.
     void dump(PrintWriter pw) {
         pw.println("ACTIVITY MANAGER SETTINGS (dumpsys activity settings) "
@@ -2092,6 +2135,11 @@
         pw.print("  "); pw.print(KEY_SHORT_FGS_ANR_EXTRA_WAIT_DURATION);
         pw.print("="); pw.println(mShortFgsAnrExtraWaitDuration);
 
+        pw.print("  "); pw.print(KEY_USE_TIERED_CACHED_ADJ);
+        pw.print("="); pw.println(USE_TIERED_CACHED_ADJ);
+        pw.print("  "); pw.print(KEY_TIERED_CACHED_ADJ_DECAY_TIME);
+        pw.print("="); pw.println(TIERED_CACHED_ADJ_DECAY_TIME);
+
         pw.println();
         if (mOverrideMaxCachedProcesses >= 0) {
             pw.print("  mOverrideMaxCachedProcesses="); pw.println(mOverrideMaxCachedProcesses);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 96d5f38..ba0f607 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8603,7 +8603,7 @@
         // On Automotive / Headless System User Mode, at this point the system user has already been
         // started and unlocked, and some of the tasks we do here have already been done. So skip
         // those in that case. The duplicate system user start is guarded in SystemServiceManager.
-        // TODO(b/242195409): this workaround shouldn't be necessary once we move the headless-user
+        // TODO(b/266158156): this workaround shouldn't be necessary once we move the headless-user
         // start logic to UserManager-land.
         mUserController.onSystemUserStarting();
 
@@ -8636,7 +8636,7 @@
 
             // Some systems - like automotive - will explicitly unlock system user then switch
             // to a secondary user.
-            // TODO(b/242195409): this workaround shouldn't be necessary once we move
+            // TODO(b/266158156): this workaround shouldn't be necessary once we move
             // the headless-user start logic to UserManager-land.
             if (isBootingSystemUser && !UserManager.isHeadlessSystemUserMode()) {
                 t.traceBegin("startHomeOnAllDisplays");
@@ -8659,26 +8659,10 @@
                 final int callingPid = Binder.getCallingPid();
                 final long ident = Binder.clearCallingIdentity();
                 try {
-                    Intent intent = new Intent(Intent.ACTION_USER_STARTED);
-                    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
-                            | Intent.FLAG_RECEIVER_FOREGROUND);
-                    intent.putExtra(Intent.EXTRA_USER_HANDLE, currentUserId);
-                    broadcastIntentLocked(null, null, null, intent,
-                            null, null, 0, null, null, null, null, null, OP_NONE,
-                            null, false, false, MY_PID, SYSTEM_UID, callingUid, callingPid,
-                            currentUserId);
-                    intent = new Intent(Intent.ACTION_USER_STARTING);
-                    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-                    intent.putExtra(Intent.EXTRA_USER_HANDLE, currentUserId);
-                    broadcastIntentLocked(null, null, null, intent, null,
-                            new IIntentReceiver.Stub() {
-                                @Override
-                                public void performReceive(Intent intent, int resultCode,
-                                        String data, Bundle extras, boolean ordered, boolean sticky,
-                                        int sendingUser) {}
-                            }, 0, null, null, new String[] {INTERACT_ACROSS_USERS}, null, null,
-                            OP_NONE, null, true, false, MY_PID, SYSTEM_UID, callingUid, callingPid,
-                            UserHandle.USER_ALL);
+                    mUserController.sendUserStartedBroadcast(
+                            currentUserId, callingUid, callingPid);
+                    mUserController.sendUserStartingBroadcast(
+                            currentUserId, callingUid, callingPid);
                 } catch (Throwable e) {
                     Slog.wtf(TAG, "Failed sending first user broadcasts", e);
                 } finally {
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index 5c7fde7..361f8bb 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -1620,6 +1620,18 @@
      */
     private void notifyFinishReceiver(@Nullable BroadcastProcessQueue queue,
             @NonNull BroadcastRecord r, int index, @NonNull Object receiver) {
+        if (r.wasDeliveryAttempted(index)) {
+            logBroadcastDeliveryEventReported(queue, r, index, receiver);
+        }
+
+        final boolean recordFinished = (r.terminalCount == r.receivers.size());
+        if (recordFinished) {
+            notifyFinishBroadcast(r);
+        }
+    }
+
+    private void logBroadcastDeliveryEventReported(@Nullable BroadcastProcessQueue queue,
+            @NonNull BroadcastRecord r, int index, @NonNull Object receiver) {
         // Report statistics for each individual receiver
         final int uid = getReceiverUid(receiver);
         final int senderUid = (r.callingUid == -1) ? Process.SYSTEM_UID : r.callingUid;
@@ -1647,11 +1659,6 @@
             FrameworkStatsLog.write(BROADCAST_DELIVERY_EVENT_REPORTED, uid, senderUid, actionName,
                     receiverType, type, dispatchDelay, receiveDelay, finishDelay, packageState);
         }
-
-        final boolean recordFinished = (r.terminalCount == r.receivers.size());
-        if (recordFinished) {
-            notifyFinishBroadcast(r);
-        }
     }
 
     private void notifyFinishBroadcast(@NonNull BroadcastRecord r) {
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index 67783be..f793c50 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -632,6 +632,18 @@
         return delivery[index];
     }
 
+    boolean wasDeliveryAttempted(int index) {
+        final int deliveryState = getDeliveryState(index);
+        switch (deliveryState) {
+            case DELIVERY_DELIVERED:
+            case DELIVERY_TIMEOUT:
+            case DELIVERY_FAILURE:
+                return true;
+            default:
+                return false;
+        }
+    }
+
     void copyEnqueueTimeFrom(@NonNull BroadcastRecord replacedBroadcast) {
         originalEnqueueClockTime = enqueueClockTime;
         enqueueTime = replacedBroadcast.enqueueTime;
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 913f151..f4685f0 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -1921,9 +1921,26 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case SET_FROZEN_PROCESS_MSG:
+                {
+                    ProcessRecord proc = (ProcessRecord) msg.obj;
+                    int pid = proc.getPid();
+                    final String name = proc.processName;
                     synchronized (mAm) {
-                        freezeProcess((ProcessRecord) msg.obj);
+                        freezeProcess(proc);
                     }
+                    try {
+                        // post-check to prevent deadlock
+                        mProcLocksReader.handleBlockingFileLocks(this);
+                    } catch (Exception e) {
+                        Slog.e(TAG_AM, "Unable to check file locks for "
+                                + name + "(" + pid + "): " + e);
+                        synchronized (mAm) {
+                            synchronized (mProcLock) {
+                                unfreezeAppLSP(proc, OomAdjuster.OOM_ADJ_REASON_NONE);
+                            }
+                        }
+                    }
+                }
                     break;
                 case REPORT_UNFREEZE_MSG:
                     int pid = msg.arg1;
@@ -2057,16 +2074,6 @@
                     }
                 });
             }
-
-            try {
-                // post-check to prevent deadlock
-                mProcLocksReader.handleBlockingFileLocks(this);
-            } catch (Exception e) {
-                Slog.e(TAG_AM, "Unable to check file locks for " + name + "(" + pid + "): " + e);
-                synchronized (mProcLock) {
-                    unfreezeAppLSP(proc, OomAdjuster.OOM_ADJ_REASON_NONE);
-                }
-            }
         }
 
         private void reportUnfreeze(int pid, int frozenDuration, String processName,
@@ -2123,22 +2130,25 @@
             if (DEBUG_FREEZER) {
                 Slog.d(TAG_AM, "Blocking file lock found: " + pids);
             }
-            synchronized (mProcLock) {
-                int pid = pids.get(0);
-                ProcessRecord app = mFrozenProcesses.get(pid);
-                ProcessRecord pr;
-                if (app != null) {
-                    for (int i = 1; i < pids.size(); i++) {
-                        int blocked = pids.get(i);
-                        synchronized (mAm.mPidsSelfLocked) {
-                            pr = mAm.mPidsSelfLocked.get(blocked);
-                        }
-                        if (pr != null && pr.mState.getCurAdj() < ProcessList.CACHED_APP_MIN_ADJ) {
-                            Slog.d(TAG_AM, app.processName + " (" + pid + ") blocks "
-                                    + pr.processName + " (" + blocked + ")");
-                            // Found at least one blocked non-cached process
-                            unfreezeAppLSP(app, OomAdjuster.OOM_ADJ_REASON_NONE);
-                            break;
+            synchronized (mAm) {
+                synchronized (mProcLock) {
+                    int pid = pids.get(0);
+                    ProcessRecord app = mFrozenProcesses.get(pid);
+                    ProcessRecord pr;
+                    if (app != null) {
+                        for (int i = 1; i < pids.size(); i++) {
+                            int blocked = pids.get(i);
+                            synchronized (mAm.mPidsSelfLocked) {
+                                pr = mAm.mPidsSelfLocked.get(blocked);
+                            }
+                            if (pr != null
+                                    && pr.mState.getCurAdj() < ProcessList.CACHED_APP_MIN_ADJ) {
+                                Slog.d(TAG_AM, app.processName + " (" + pid + ") blocks "
+                                        + pr.processName + " (" + blocked + ")");
+                                // Found at least one blocked non-cached process
+                                unfreezeAppLSP(app, OomAdjuster.OOM_ADJ_REASON_NONE);
+                                break;
+                            }
                         }
                     }
                 }
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 32db33d..b68d993 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1073,135 +1073,165 @@
     @GuardedBy({"mService", "mProcLock"})
     private void assignCachedAdjIfNecessary(ArrayList<ProcessRecord> lruList) {
         final int numLru = lruList.size();
+        if (mConstants.USE_TIERED_CACHED_ADJ) {
+            final long now = SystemClock.uptimeMillis();
+            for (int i = numLru - 1; i >= 0; i--) {
+                ProcessRecord app = lruList.get(i);
+                final ProcessStateRecord state = app.mState;
+                final ProcessCachedOptimizerRecord opt = app.mOptRecord;
+                if (!app.isKilledByAm() && app.getThread() != null && state.getCurAdj()
+                        >= UNKNOWN_ADJ) {
+                    final ProcessServiceRecord psr = app.mServices;
+                    int targetAdj = CACHED_APP_MIN_ADJ;
 
-        // First update the OOM adjustment for each of the
-        // application processes based on their current state.
-        int curCachedAdj = CACHED_APP_MIN_ADJ;
-        int nextCachedAdj = curCachedAdj + (CACHED_APP_IMPORTANCE_LEVELS * 2);
-        int curCachedImpAdj = 0;
-        int curEmptyAdj = CACHED_APP_MIN_ADJ + CACHED_APP_IMPORTANCE_LEVELS;
-        int nextEmptyAdj = curEmptyAdj + (CACHED_APP_IMPORTANCE_LEVELS * 2);
+                    if (opt != null && opt.isFreezeExempt()) {
+                        // BIND_WAIVE_PRIORITY and the like get oom_adj 900
+                        targetAdj += 0;
+                    } else if ((state.getSetAdj() >= CACHED_APP_MIN_ADJ)
+                            && (state.getLastStateTime()
+                                    + mConstants.TIERED_CACHED_ADJ_DECAY_TIME) < now) {
+                        // Older cached apps get 950
+                        targetAdj += 50;
+                    } else {
+                        // Newer cached apps get 910
+                        targetAdj += 10;
+                    }
+                    state.setCurRawAdj(targetAdj);
+                    state.setCurAdj(psr.modifyRawOomAdj(targetAdj));
+                }
+            }
+        } else {
+            // First update the OOM adjustment for each of the
+            // application processes based on their current state.
+            int curCachedAdj = CACHED_APP_MIN_ADJ;
+            int nextCachedAdj = curCachedAdj + (CACHED_APP_IMPORTANCE_LEVELS * 2);
+            int curCachedImpAdj = 0;
+            int curEmptyAdj = CACHED_APP_MIN_ADJ + CACHED_APP_IMPORTANCE_LEVELS;
+            int nextEmptyAdj = curEmptyAdj + (CACHED_APP_IMPORTANCE_LEVELS * 2);
 
-        final int emptyProcessLimit = mConstants.CUR_MAX_EMPTY_PROCESSES;
-        final int cachedProcessLimit = mConstants.CUR_MAX_CACHED_PROCESSES
-                - emptyProcessLimit;
-        // Let's determine how many processes we have running vs.
-        // how many slots we have for background processes; we may want
-        // to put multiple processes in a slot of there are enough of
-        // them.
-        int numEmptyProcs = numLru - mNumNonCachedProcs - mNumCachedHiddenProcs;
-        if (numEmptyProcs > cachedProcessLimit) {
-            // If there are more empty processes than our limit on cached
-            // processes, then use the cached process limit for the factor.
-            // This ensures that the really old empty processes get pushed
-            // down to the bottom, so if we are running low on memory we will
-            // have a better chance at keeping around more cached processes
-            // instead of a gazillion empty processes.
-            numEmptyProcs = cachedProcessLimit;
-        }
-        int cachedFactor = (mNumCachedHiddenProcs > 0 ? (mNumCachedHiddenProcs + mNumSlots - 1) : 1)
-                / mNumSlots;
-        if (cachedFactor < 1) cachedFactor = 1;
+            final int emptyProcessLimit = mConstants.CUR_MAX_EMPTY_PROCESSES;
+            final int cachedProcessLimit = mConstants.CUR_MAX_CACHED_PROCESSES
+                                           - emptyProcessLimit;
+            // Let's determine how many processes we have running vs.
+            // how many slots we have for background processes; we may want
+            // to put multiple processes in a slot of there are enough of
+            // them.
+            int numEmptyProcs = numLru - mNumNonCachedProcs - mNumCachedHiddenProcs;
+            if (numEmptyProcs > cachedProcessLimit) {
+                // If there are more empty processes than our limit on cached
+                // processes, then use the cached process limit for the factor.
+                // This ensures that the really old empty processes get pushed
+                // down to the bottom, so if we are running low on memory we will
+                // have a better chance at keeping around more cached processes
+                // instead of a gazillion empty processes.
+                numEmptyProcs = cachedProcessLimit;
+            }
+            int cachedFactor = (mNumCachedHiddenProcs > 0
+                    ? (mNumCachedHiddenProcs + mNumSlots - 1) : 1)
+                               / mNumSlots;
+            if (cachedFactor < 1) cachedFactor = 1;
 
-        int emptyFactor = (numEmptyProcs + mNumSlots - 1) / mNumSlots;
-        if (emptyFactor < 1) emptyFactor = 1;
+            int emptyFactor = (numEmptyProcs + mNumSlots - 1) / mNumSlots;
+            if (emptyFactor < 1) emptyFactor = 1;
 
-        int stepCached = -1;
-        int stepEmpty = -1;
-        int lastCachedGroup = 0;
-        int lastCachedGroupImportance = 0;
-        int lastCachedGroupUid = 0;
+            int stepCached = -1;
+            int stepEmpty = -1;
+            int lastCachedGroup = 0;
+            int lastCachedGroupImportance = 0;
+            int lastCachedGroupUid = 0;
 
-        for (int i = numLru - 1; i >= 0; i--) {
-            ProcessRecord app = lruList.get(i);
-            final ProcessStateRecord state = app.mState;
-            // If we haven't yet assigned the final cached adj
-            // to the process, do that now.
-            if (!app.isKilledByAm() && app.getThread() != null && state.getCurAdj()
+
+            for (int i = numLru - 1; i >= 0; i--) {
+                ProcessRecord app = lruList.get(i);
+                final ProcessStateRecord state = app.mState;
+                // If we haven't yet assigned the final cached adj
+                // to the process, do that now.
+                if (!app.isKilledByAm() && app.getThread() != null && state.getCurAdj()
                     >= UNKNOWN_ADJ) {
-                final ProcessServiceRecord psr = app.mServices;
-                switch (state.getCurProcState()) {
-                    case PROCESS_STATE_CACHED_ACTIVITY:
-                    case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
-                    case ActivityManager.PROCESS_STATE_CACHED_RECENT:
-                        // Figure out the next cached level, taking into account groups.
-                        boolean inGroup = false;
-                        final int connectionGroup = psr.getConnectionGroup();
-                        if (connectionGroup != 0) {
-                            final int connectionImportance = psr.getConnectionImportance();
-                            if (lastCachedGroupUid == app.uid
+                    final ProcessServiceRecord psr = app.mServices;
+                    switch (state.getCurProcState()) {
+                        case PROCESS_STATE_CACHED_ACTIVITY:
+                        case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
+                        case ActivityManager.PROCESS_STATE_CACHED_RECENT:
+                            // Figure out the next cached level, taking into account groups.
+                            boolean inGroup = false;
+                            final int connectionGroup = psr.getConnectionGroup();
+                            if (connectionGroup != 0) {
+                                final int connectionImportance = psr.getConnectionImportance();
+                                if (lastCachedGroupUid == app.uid
                                     && lastCachedGroup == connectionGroup) {
-                                // This is in the same group as the last process, just tweak
-                                // adjustment by importance.
-                                if (connectionImportance > lastCachedGroupImportance) {
-                                    lastCachedGroupImportance = connectionImportance;
-                                    if (curCachedAdj < nextCachedAdj
+                                    // This is in the same group as the last process, just tweak
+                                    // adjustment by importance.
+                                    if (connectionImportance > lastCachedGroupImportance) {
+                                        lastCachedGroupImportance = connectionImportance;
+                                        if (curCachedAdj < nextCachedAdj
                                             && curCachedAdj < CACHED_APP_MAX_ADJ) {
-                                        curCachedImpAdj++;
+                                            curCachedImpAdj++;
+                                        }
+                                    }
+                                    inGroup = true;
+                                } else {
+                                    lastCachedGroupUid = app.uid;
+                                    lastCachedGroup = connectionGroup;
+                                    lastCachedGroupImportance = connectionImportance;
+                                }
+                            }
+                            if (!inGroup && curCachedAdj != nextCachedAdj) {
+                                stepCached++;
+                                curCachedImpAdj = 0;
+                                if (stepCached >= cachedFactor) {
+                                    stepCached = 0;
+                                    curCachedAdj = nextCachedAdj;
+                                    nextCachedAdj += CACHED_APP_IMPORTANCE_LEVELS * 2;
+                                    if (nextCachedAdj > CACHED_APP_MAX_ADJ) {
+                                        nextCachedAdj = CACHED_APP_MAX_ADJ;
                                     }
                                 }
-                                inGroup = true;
-                            } else {
-                                lastCachedGroupUid = app.uid;
-                                lastCachedGroup = connectionGroup;
-                                lastCachedGroupImportance = connectionImportance;
                             }
-                        }
-                        if (!inGroup && curCachedAdj != nextCachedAdj) {
-                            stepCached++;
-                            curCachedImpAdj = 0;
-                            if (stepCached >= cachedFactor) {
-                                stepCached = 0;
-                                curCachedAdj = nextCachedAdj;
-                                nextCachedAdj += CACHED_APP_IMPORTANCE_LEVELS * 2;
-                                if (nextCachedAdj > CACHED_APP_MAX_ADJ) {
-                                    nextCachedAdj = CACHED_APP_MAX_ADJ;
+                            // This process is a cached process holding activities...
+                            // assign it the next cached value for that type, and then
+                            // step that cached level.
+                            state.setCurRawAdj(curCachedAdj + curCachedImpAdj);
+                            state.setCurAdj(psr.modifyRawOomAdj(curCachedAdj + curCachedImpAdj));
+                            if (DEBUG_LRU) {
+                                Slog.d(TAG_LRU, "Assigning activity LRU #" + i
+                                        + " adj: " + state.getCurAdj()
+                                        + " (curCachedAdj=" + curCachedAdj
+                                        + " curCachedImpAdj=" + curCachedImpAdj + ")");
+                            }
+                            break;
+                        default:
+                            // Figure out the next cached level.
+                            if (curEmptyAdj != nextEmptyAdj) {
+                                stepEmpty++;
+                                if (stepEmpty >= emptyFactor) {
+                                    stepEmpty = 0;
+                                    curEmptyAdj = nextEmptyAdj;
+                                    nextEmptyAdj += CACHED_APP_IMPORTANCE_LEVELS * 2;
+                                    if (nextEmptyAdj > CACHED_APP_MAX_ADJ) {
+                                        nextEmptyAdj = CACHED_APP_MAX_ADJ;
+                                    }
                                 }
                             }
-                        }
-                        // This process is a cached process holding activities...
-                        // assign it the next cached value for that type, and then
-                        // step that cached level.
-                        state.setCurRawAdj(curCachedAdj + curCachedImpAdj);
-                        state.setCurAdj(psr.modifyRawOomAdj(curCachedAdj + curCachedImpAdj));
-                        if (DEBUG_LRU) {
-                            Slog.d(TAG_LRU, "Assigning activity LRU #" + i
-                                    + " adj: " + state.getCurAdj()
-                                    + " (curCachedAdj=" + curCachedAdj
-                                    + " curCachedImpAdj=" + curCachedImpAdj + ")");
-                        }
-                        break;
-                    default:
-                        // Figure out the next cached level.
-                        if (curEmptyAdj != nextEmptyAdj) {
-                            stepEmpty++;
-                            if (stepEmpty >= emptyFactor) {
-                                stepEmpty = 0;
-                                curEmptyAdj = nextEmptyAdj;
-                                nextEmptyAdj += CACHED_APP_IMPORTANCE_LEVELS * 2;
-                                if (nextEmptyAdj > CACHED_APP_MAX_ADJ) {
-                                    nextEmptyAdj = CACHED_APP_MAX_ADJ;
-                                }
+                            // For everything else, assign next empty cached process
+                            // level and bump that up.  Note that this means that
+                            // long-running services that have dropped down to the
+                            // cached level will be treated as empty (since their process
+                            // state is still as a service), which is what we want.
+                            state.setCurRawAdj(curEmptyAdj);
+                            state.setCurAdj(psr.modifyRawOomAdj(curEmptyAdj));
+                            if (DEBUG_LRU) {
+                                Slog.d(TAG_LRU, "Assigning empty LRU #" + i
+                                        + " adj: " + state.getCurAdj()
+                                        + " (curEmptyAdj=" + curEmptyAdj
+                                        + ")");
                             }
-                        }
-                        // For everything else, assign next empty cached process
-                        // level and bump that up.  Note that this means that
-                        // long-running services that have dropped down to the
-                        // cached level will be treated as empty (since their process
-                        // state is still as a service), which is what we want.
-                        state.setCurRawAdj(curEmptyAdj);
-                        state.setCurAdj(psr.modifyRawOomAdj(curEmptyAdj));
-                        if (DEBUG_LRU) {
-                            Slog.d(TAG_LRU, "Assigning empty LRU #" + i
-                                    + " adj: " + state.getCurAdj() + " (curEmptyAdj=" + curEmptyAdj
-                                    + ")");
-                        }
-                        break;
+                            break;
+                    }
                 }
             }
         }
     }
-
     private long mNextNoKillDebugMessageTime;
 
     private double mLastFreeSwapPercent = 1.00;
@@ -2018,25 +2048,37 @@
                 }
             }
         }
-
         if (state.getCachedIsPreviousProcess() && state.getCachedHasActivities()) {
-            if (adj > PREVIOUS_APP_ADJ) {
-                // This was the previous process that showed UI to the user.
-                // We want to try to keep it around more aggressively, to give
-                // a good experience around switching between two apps.
-                adj = PREVIOUS_APP_ADJ;
-                schedGroup = SCHED_GROUP_BACKGROUND;
-                state.setCached(false);
-                state.setAdjType("previous");
-                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to prev: " + app);
-                }
-            }
-            if (procState > PROCESS_STATE_LAST_ACTIVITY) {
+            // This was the previous process that showed UI to the user.  We want to
+            // try to keep it around more aggressively, to give a good experience
+            // around switching between two apps. However, we don't want to keep the
+            // process in this privileged state indefinitely. Eventually, allow the
+            // app to be demoted to cached.
+            if ((state.getSetProcState() == PROCESS_STATE_LAST_ACTIVITY
+                    && (state.getLastStateTime() + mConstants.MAX_PREVIOUS_TIME) < now)) {
                 procState = PROCESS_STATE_LAST_ACTIVITY;
-                state.setAdjType("previous");
+                schedGroup = SCHED_GROUP_BACKGROUND;
+                state.setAdjType("previous-expired");
+                adj = CACHED_APP_MIN_ADJ;
                 if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
-                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to prev: " + app);
+                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Expire prev adj: " + app);
+                }
+            } else {
+                if (adj > PREVIOUS_APP_ADJ) {
+                    adj = PREVIOUS_APP_ADJ;
+                    schedGroup = SCHED_GROUP_BACKGROUND;
+                    state.setCached(false);
+                    state.setAdjType("previous");
+                    if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                        reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to prev: " + app);
+                    }
+                }
+                if (procState > PROCESS_STATE_LAST_ACTIVITY) {
+                    procState = PROCESS_STATE_LAST_ACTIVITY;
+                    state.setAdjType("previous");
+                    if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+                        reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to prev: " + app);
+                    }
                 }
             }
         }
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 14ecf9f..daf227c 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -69,7 +69,8 @@
     @Overridable
     private static final long DEFAULT_RESCIND_BAL_PRIVILEGES_FROM_PENDING_INTENT_SENDER = 244637991;
     private static final String ENABLE_DEFAULT_RESCIND_BAL_PRIVILEGES_FROM_PENDING_INTENT_SENDER =
-            "enable_default_rescind_bal_privileges_from_pending_intent_sender";
+            "DefaultRescindBalPrivilegesFromPendingIntentSender__"
+                    + "enable_default_rescind_bal_privileges_from_pending_intent_sender";
 
     public static final int FLAG_ACTIVITY_SENDER = 1 << 0;
     public static final int FLAG_BROADCAST_SENDER = 1 << 1;
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 0e49c66..1b37883 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -365,13 +365,13 @@
     private volatile ArraySet<String> mCurWaitingUserSwitchCallbacks;
 
     /**
-     * Messages for for switching from {@link android.os.UserHandle#SYSTEM}.
+     * Messages for switching from {@link android.os.UserHandle#SYSTEM}.
      */
     @GuardedBy("mLock")
     private String mSwitchingFromSystemUserMessage;
 
     /**
-     * Messages for for switching to {@link android.os.UserHandle#SYSTEM}.
+     * Messages for switching to {@link android.os.UserHandle#SYSTEM}.
      */
     @GuardedBy("mLock")
     private String mSwitchingToSystemUserMessage;
@@ -384,6 +384,16 @@
 
     private final LockPatternUtils mLockPatternUtils;
 
+    // TODO(b/266158156): remove this once we improve/refactor the way broadcasts are sent for
+    //  the system user in HSUM.
+    @GuardedBy("mLock")
+    private boolean mIsBroadcastSentForSystemUserStarted;
+
+    // TODO(b/266158156): remove this once we improve/refactor the way broadcasts are sent for
+    //  the system user in HSUM.
+    @GuardedBy("mLock")
+    private boolean mIsBroadcastSentForSystemUserStarting;
+
     volatile boolean mBootCompleted;
 
     /**
@@ -635,7 +645,7 @@
                 // user transitions to RUNNING_LOCKED.  However, in "headless system user mode", the
                 // system user is explicitly started before the device has finished booting.  In
                 // that case, we need to wait until onBootComplete() to send the broadcast.
-                if (!(UserManager.isHeadlessSystemUserMode() && uss.mHandle.isSystem())) {
+                if (!(mInjector.isHeadlessSystemUserMode() && uss.mHandle.isSystem())) {
                     // ACTION_LOCKED_BOOT_COMPLETED
                     sendLockedBootCompletedBroadcast(resultTo, userId);
                 }
@@ -1823,15 +1833,17 @@
                 needStart = false;
             }
 
-            if (needStart) {
-                // Send USER_STARTED broadcast
-                Intent intent = new Intent(Intent.ACTION_USER_STARTED);
-                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
-                        | Intent.FLAG_RECEIVER_FOREGROUND);
-                intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
-                mInjector.broadcastIntent(intent,
-                        null, null, 0, null, null, null, AppOpsManager.OP_NONE,
-                        null, false, false, MY_PID, SYSTEM_UID, callingUid, callingPid, userId);
+            // In most cases, broadcast for the system user starting/started is sent by
+            // ActivityManagerService#systemReady(). However on some HSUM devices (e.g. tablets)
+            // the user switches from the system user to a secondary user while running
+            // ActivityManagerService#systemReady(), thus broadcast is not sent for the system user.
+            // Therefore we send the broadcast for the system user here as well in HSUM.
+            // TODO(b/266158156): Improve/refactor the way broadcasts are sent for the system user
+            // in HSUM. Ideally it'd be best to have one single place that sends this notification.
+            final boolean isSystemUserInHeadlessMode = (userId == UserHandle.USER_SYSTEM)
+                    && mInjector.isHeadlessSystemUserMode();
+            if (needStart || isSystemUserInHeadlessMode) {
+                sendUserStartedBroadcast(userId, callingUid, callingPid);
             }
             t.traceEnd();
 
@@ -1845,23 +1857,9 @@
                 t.traceEnd();
             }
 
-            if (needStart) {
+            if (needStart || isSystemUserInHeadlessMode) {
                 t.traceBegin("sendRestartBroadcast");
-                Intent intent = new Intent(Intent.ACTION_USER_STARTING);
-                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-                intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
-                mInjector.broadcastIntent(intent,
-                        null, new IIntentReceiver.Stub() {
-                            @Override
-                            public void performReceive(Intent intent, int resultCode,
-                                    String data, Bundle extras, boolean ordered,
-                                    boolean sticky,
-                                    int sendingUser) throws RemoteException {
-                            }
-                        }, 0, null, null,
-                        new String[]{INTERACT_ACROSS_USERS}, AppOpsManager.OP_NONE,
-                        null, true, false, MY_PID, SYSTEM_UID, callingUid, callingPid,
-                        UserHandle.USER_ALL);
+                sendUserStartingBroadcast(userId, callingUid, callingPid);
                 t.traceEnd();
             }
         } finally {
@@ -2283,6 +2281,62 @@
         EventLogTags.writeAmSwitchUser(newUserId);
     }
 
+    // The two methods sendUserStartedBroadcast() and sendUserStartingBroadcast()
+    // could be merged for better reuse. However, the params they are calling broadcastIntent()
+    // with are different - resultCode receiver, permissions, ordered, and userId, etc. Therefore,
+    // we decided to keep two separate methods for better code readability/clarity.
+    // TODO(b/266158156): Improve/refactor the way broadcasts are sent for the system user
+    // in HSUM. Ideally it'd be best to have one single place that sends this notification.
+    /** Sends {@code ACTION_USER_STARTED} broadcast. */
+    void sendUserStartedBroadcast(@UserIdInt int userId, int callingUid, int callingPid) {
+        if (userId == UserHandle.USER_SYSTEM) {
+            synchronized (mLock) {
+                // Make sure that the broadcast is sent only once for the system user.
+                if (mIsBroadcastSentForSystemUserStarted) {
+                    return;
+                }
+                mIsBroadcastSentForSystemUserStarted = true;
+            }
+        }
+        final Intent intent = new Intent(Intent.ACTION_USER_STARTED);
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
+                | Intent.FLAG_RECEIVER_FOREGROUND);
+        intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+        mInjector.broadcastIntent(intent, /* resolvedType= */ null, /* resultTo= */ null,
+                /* resultCode= */ 0, /* resultData= */ null, /* resultExtras= */ null,
+                /* requiredPermissions= */ null, AppOpsManager.OP_NONE, /* bOptions= */ null,
+                /* ordered= */ false, /* sticky= */ false, MY_PID, SYSTEM_UID,
+                callingUid, callingPid, userId);
+    }
+
+    /** Sends {@code ACTION_USER_STARTING} broadcast. */
+    void sendUserStartingBroadcast(@UserIdInt int userId, int callingUid, int callingPid) {
+        if (userId == UserHandle.USER_SYSTEM) {
+            synchronized (mLock) {
+                // Make sure that the broadcast is sent only once for the system user.
+                if (mIsBroadcastSentForSystemUserStarting) {
+                    return;
+                }
+                mIsBroadcastSentForSystemUserStarting = true;
+            }
+        }
+        final Intent intent = new Intent(Intent.ACTION_USER_STARTING);
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+        intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+        mInjector.broadcastIntent(intent, /* resolvedType= */ null,
+                new IIntentReceiver.Stub() {
+                    @Override
+                    public void performReceive(Intent intent, int resultCode,
+                            String data, Bundle extras, boolean ordered,
+                            boolean sticky,
+                            int sendingUser) throws RemoteException {
+                    }
+                }, /* resultCode= */ 0, /* resultData= */ null, /* resultExtras= */ null,
+                new String[]{INTERACT_ACROSS_USERS}, AppOpsManager.OP_NONE, /* bOptions= */ null,
+                /* ordered= */ true, /* sticky= */ false, MY_PID, SYSTEM_UID,
+                callingUid, callingPid, UserHandle.USER_ALL);
+    }
+
     void sendUserSwitchBroadcasts(int oldUserId, int newUserId) {
         final int callingUid = Binder.getCallingUid();
         final int callingPid = Binder.getCallingPid();
@@ -2568,7 +2622,7 @@
         for (int i = 0; i < startedUsers.size(); i++) {
             int userId = startedUsers.keyAt(i);
             UserState uss = startedUsers.valueAt(i);
-            if (!UserManager.isHeadlessSystemUserMode()) {
+            if (!mInjector.isHeadlessSystemUserMode()) {
                 finishUserBoot(uss, resultTo);
             } else {
                 if (userId == UserHandle.USER_SYSTEM) {
@@ -2589,9 +2643,9 @@
         mInjector.reportCurWakefulnessUsageEvent();
     }
 
-    // TODO(b/242195409): remove this method if initial system user boot logic is refactored?
+    // TODO(b/266158156): remove this method if initial system user boot logic is refactored?
     void onSystemUserStarting() {
-        if (!UserManager.isHeadlessSystemUserMode()) {
+        if (!mInjector.isHeadlessSystemUserMode()) {
             // Don't need to call on HSUM because it will be called when the system user is
             // restarted on background
             mInjector.onUserStarting(UserHandle.USER_SYSTEM);
@@ -3059,6 +3113,10 @@
             pw.println("  mMaxRunningUsers:" + mMaxRunningUsers);
             pw.println("  mUserSwitchUiEnabled:" + mUserSwitchUiEnabled);
             pw.println("  mInitialized:" + mInitialized);
+            pw.println("  mIsBroadcastSentForSystemUserStarted:"
+                    + mIsBroadcastSentForSystemUserStarted);
+            pw.println("  mIsBroadcastSentForSystemUserStarting:"
+                    + mIsBroadcastSentForSystemUserStarting);
             if (mSwitchingFromSystemUserMessage != null) {
                 pw.println("  mSwitchingFromSystemUserMessage: " + mSwitchingFromSystemUserMessage);
             }
@@ -3749,6 +3807,10 @@
             }, /* message= */ null);
         }
 
+        boolean isHeadlessSystemUserMode() {
+            return UserManager.isHeadlessSystemUserMode();
+        }
+
         boolean isUsersOnSecondaryDisplaysEnabled() {
             return UserManager.isVisibleBackgroundUsersEnabled();
         }
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 3304fb0..1268156 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -1567,6 +1567,7 @@
                 case MSG_I_BT_SERVICE_DISCONNECTED_PROFILE:
                     if (msg.arg1 != BluetoothProfile.HEADSET) {
                         synchronized (mDeviceStateLock) {
+                            mBtHelper.onBtProfileDisconnected(msg.arg1);
                             mDeviceInventory.onBtProfileDisconnected(msg.arg1);
                         }
                     } else {
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index df65dbd..2dcdc54 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -278,7 +278,11 @@
         }
         AudioService.sVolumeLogger.enqueue(new AudioServiceEvents.VolumeEvent(
                 AudioServiceEvents.VolumeEvent.VOL_SET_AVRCP_VOL, index));
-        mA2dp.setAvrcpAbsoluteVolume(index);
+        try {
+            mA2dp.setAvrcpAbsoluteVolume(index);
+        } catch (Exception e) {
+            Log.e(TAG, "Exception while changing abs volume", e);
+        }
     }
 
     /*package*/ synchronized @AudioSystem.AudioFormatNativeEnumForBtCodec int getA2dpCodec(
@@ -286,7 +290,12 @@
         if (mA2dp == null) {
             return AudioSystem.AUDIO_FORMAT_DEFAULT;
         }
-        final BluetoothCodecStatus btCodecStatus = mA2dp.getCodecStatus(device);
+        BluetoothCodecStatus btCodecStatus = null;
+        try {
+            btCodecStatus = mA2dp.getCodecStatus(device);
+        } catch (Exception e) {
+            Log.e(TAG, "Exception while getting status of " + device, e);
+        }
         if (btCodecStatus == null) {
             return AudioSystem.AUDIO_FORMAT_DEFAULT;
         }
@@ -420,7 +429,11 @@
         }
         AudioService.sVolumeLogger.enqueue(new AudioServiceEvents.VolumeEvent(
                 AudioServiceEvents.VolumeEvent.VOL_SET_LE_AUDIO_VOL, index, maxIndex));
-        mLeAudio.setVolume(volume);
+        try {
+            mLeAudio.setVolume(volume);
+        } catch (Exception e) {
+            Log.e(TAG, "Exception while setting LE volume", e);
+        }
     }
 
     /*package*/ synchronized void setHearingAidVolume(int index, int streamType,
@@ -446,7 +459,11 @@
             AudioService.sVolumeLogger.enqueue(new AudioServiceEvents.VolumeEvent(
                     AudioServiceEvents.VolumeEvent.VOL_SET_HEARING_AID_VOL, index, gainDB));
         }
-        mHearingAid.setVolume(gainDB);
+        try {
+            mHearingAid.setVolume(gainDB);
+        } catch (Exception e) {
+            Log.i(TAG, "Exception while setting hearing aid volume", e);
+        }
     }
 
     /*package*/ synchronized void onBroadcastScoConnectionState(int state) {
@@ -471,7 +488,7 @@
     }
 
     // @GuardedBy("AudioDeviceBroker.mSetModeLock")
-    @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+    //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
     /*package*/ synchronized void resetBluetoothSco() {
         mScoAudioState = SCO_STATE_INACTIVE;
         broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
@@ -486,6 +503,35 @@
         mBluetoothHeadset = null;
     }
 
+    //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+    /*package*/ synchronized void onBtProfileDisconnected(int profile) {
+        switch (profile) {
+            case BluetoothProfile.A2DP:
+                mA2dp = null;
+                break;
+            case BluetoothProfile.HEARING_AID:
+                mHearingAid = null;
+                break;
+            case BluetoothProfile.LE_AUDIO:
+                mLeAudio = null;
+                break;
+
+            case BluetoothProfile.A2DP_SINK:
+            case BluetoothProfile.LE_AUDIO_BROADCAST:
+                // shouldn't be received here as profile doesn't involve BtHelper
+                Log.e(TAG, "onBtProfileDisconnected: Not a profile handled by BtHelper "
+                        + BluetoothProfile.getProfileName(profile));
+                break;
+
+            default:
+                // Not a valid profile to disconnect
+                Log.e(TAG, "onBtProfileDisconnected: Not a valid profile to disconnect "
+                        + BluetoothProfile.getProfileName(profile));
+                break;
+        }
+    }
+
+    @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
     /*package*/ synchronized void onBtProfileConnected(int profile, BluetoothProfile proxy) {
         if (profile == BluetoothProfile.HEADSET) {
             onHeadsetProfileConnected((BluetoothHeadset) proxy);
@@ -671,7 +717,6 @@
                 public void onServiceConnected(int profile, BluetoothProfile proxy) {
                     switch(profile) {
                         case BluetoothProfile.A2DP:
-                        case BluetoothProfile.A2DP_SINK:
                         case BluetoothProfile.HEADSET:
                         case BluetoothProfile.HEARING_AID:
                         case BluetoothProfile.LE_AUDIO:
@@ -681,6 +726,10 @@
                             mDeviceBroker.postBtProfileConnected(profile, proxy);
                             break;
 
+                        case BluetoothProfile.A2DP_SINK:
+                            // no A2DP sink functionality handled by BtHelper
+                        case BluetoothProfile.LE_AUDIO_BROADCAST:
+                            // no broadcast functionality handled by BtHelper
                         default:
                             break;
                     }
@@ -689,14 +738,19 @@
 
                     switch (profile) {
                         case BluetoothProfile.A2DP:
-                        case BluetoothProfile.A2DP_SINK:
                         case BluetoothProfile.HEADSET:
                         case BluetoothProfile.HEARING_AID:
                         case BluetoothProfile.LE_AUDIO:
-                        case BluetoothProfile.LE_AUDIO_BROADCAST:
+                            AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
+                                    "BT profile service: disconnecting "
+                                        + BluetoothProfile.getProfileName(profile) + " profile"));
                             mDeviceBroker.postBtProfileDisconnected(profile);
                             break;
 
+                        case BluetoothProfile.A2DP_SINK:
+                            // no A2DP sink functionality handled by BtHelper
+                        case BluetoothProfile.LE_AUDIO_BROADCAST:
+                            // no broadcast functionality handled by BtHelper
                         default:
                             break;
                     }
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 850f6da..81e550e 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -466,6 +466,8 @@
     private boolean mIsDocked;
     private boolean mIsDreaming;
 
+    private boolean mBootCompleted = false;
+
     private final BroadcastReceiver mIdleModeReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -602,6 +604,12 @@
                 }
             }
         } else if (phase == PHASE_BOOT_COMPLETED) {
+            synchronized (mSyncRoot) {
+                mBootCompleted = true;
+                for (int i = 0; i < mDisplayPowerControllers.size(); i++) {
+                    mDisplayPowerControllers.valueAt(i).onBootCompleted();
+                }
+            }
             mDisplayModeDirector.onBootCompleted();
             mLogicalDisplayMapper.onBootCompleted();
         }
@@ -2998,12 +3006,12 @@
             displayPowerController = new DisplayPowerController2(
                     mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
                     mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
-                    () -> handleBrightnessChange(display), hbmMetadata);
+                    () -> handleBrightnessChange(display), hbmMetadata, mBootCompleted);
         } else {
             displayPowerController = new DisplayPowerController(
                     mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
                     mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
-                    () -> handleBrightnessChange(display), hbmMetadata);
+                    () -> handleBrightnessChange(display), hbmMetadata, mBootCompleted);
         }
         mDisplayPowerControllers.append(display.getDisplayIdLocked(), displayPowerController);
     }
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 82e6e30..d4877eb 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -139,6 +139,7 @@
     private static final int MSG_BRIGHTNESS_RAMP_DONE = 12;
     private static final int MSG_STATSD_HBM_BRIGHTNESS = 13;
     private static final int MSG_SWITCH_USER = 14;
+    private static final int MSG_BOOT_COMPLETED = 15;
 
     private static final int PROXIMITY_UNKNOWN = -1;
     private static final int PROXIMITY_NEGATIVE = 0;
@@ -518,6 +519,8 @@
     private final SparseArray<DisplayPowerControllerInterface> mDisplayBrightnessFollowers =
             new SparseArray<>();
 
+    private boolean mBootCompleted;
+
     /**
      * Creates the display power controller.
      */
@@ -525,7 +528,8 @@
             DisplayPowerCallbacks callbacks, Handler handler,
             SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay,
             BrightnessTracker brightnessTracker, BrightnessSetting brightnessSetting,
-            Runnable onBrightnessChangeRunnable, HighBrightnessModeMetadata hbmMetadata) {
+            Runnable onBrightnessChangeRunnable, HighBrightnessModeMetadata hbmMetadata,
+            boolean bootCompleted) {
 
         mInjector = injector != null ? injector : new Injector();
         mClock = mInjector.getClock();
@@ -662,6 +666,7 @@
         mTemporaryAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT;
         mPendingAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT;
 
+        mBootCompleted = bootCompleted;
     }
 
     private void applyReduceBrightColorsSplineAdjustment() {
@@ -1188,17 +1193,15 @@
             }
             loadScreenOffBrightnessSensor();
             int[] sensorValueToLux = mDisplayDeviceConfig.getScreenOffBrightnessSensorValueToLux();
-            // TODO (b/265793751): Don't instantiate ScreenOffBrightnessSensorController if this is
-            // a complementary display
             if (mScreenOffBrightnessSensor != null && sensorValueToLux != null) {
-                mScreenOffBrightnessSensorController = new ScreenOffBrightnessSensorController(
-                        mSensorManager,
-                        mScreenOffBrightnessSensor,
-                        mHandler,
-                        SystemClock::uptimeMillis,
-                        sensorValueToLux,
-                        mInteractiveModeBrightnessMapper
-                );
+                mScreenOffBrightnessSensorController =
+                        mInjector.getScreenOffBrightnessSensorController(
+                                mSensorManager,
+                                mScreenOffBrightnessSensor,
+                                mHandler,
+                                SystemClock::uptimeMillis,
+                                sensorValueToLux,
+                                mInteractiveModeBrightnessMapper);
             }
         } else {
             mUseSoftwareAutoBrightnessConfig = false;
@@ -1390,8 +1393,9 @@
 
         if (mScreenOffBrightnessSensorController != null) {
             mScreenOffBrightnessSensorController.setLightSensorEnabled(mUseAutoBrightness
-                    && (state == Display.STATE_OFF || (state == Display.STATE_DOZE
-                    && !mAllowAutoBrightnessWhileDozingConfig)));
+                    && mIsEnabled && (state == Display.STATE_OFF || (state == Display.STATE_DOZE
+                    && !mAllowAutoBrightnessWhileDozingConfig))
+                    && mLeadDisplayId == Layout.NO_LEAD_DISPLAY);
         }
 
         boolean skipRampBecauseOfProximityChangeToNegative = false;
@@ -1448,7 +1452,7 @@
 
         // Initialize things the first time the power state is changed.
         if (mustInitialize) {
-            initialize(state);
+            initialize(readyToUpdateDisplayState() ? state : Display.STATE_UNKNOWN);
         }
 
         // Animate the screen state change unless already animating.
@@ -2154,7 +2158,8 @@
                 }
             }
 
-            if (!reportOnly && mPowerState.getScreenState() != state) {
+            if (!reportOnly && mPowerState.getScreenState() != state
+                    && readyToUpdateDisplayState()) {
                 Trace.traceCounter(Trace.TRACE_TAG_POWER, "ScreenState", state);
                 // TODO(b/153319140) remove when we can get this from the above trace invocation
                 SystemProperties.set("debug.tracing.screen_state", String.valueOf(state));
@@ -2538,11 +2543,11 @@
                 mContext.getContentResolver(),
                 Settings.System.SCREEN_BRIGHTNESS_MODE,
                 Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, UserHandle.USER_CURRENT);
-        mHandler.post(() -> {
+        mHandler.postAtTime(() -> {
             mUseAutoBrightness = screenBrightnessModeSetting
                     == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC;
             updatePowerState();
-        });
+        }, mClock.uptimeMillis());
     }
 
     private float getAutoBrightnessAdjustmentSetting() {
@@ -2567,6 +2572,12 @@
         mBrightnessSetting.setBrightness(brightnessValue);
     }
 
+    @Override
+    public void onBootCompleted() {
+        Message msg = mHandler.obtainMessage(MSG_BOOT_COMPLETED);
+        mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
+    }
+
     private void updateScreenBrightnessSetting(float brightnessValue) {
         if (!isValidBrightnessValue(brightnessValue)
                 || brightnessValue == mCurrentScreenBrightnessSetting) {
@@ -2712,6 +2723,17 @@
         }
     };
 
+    /**
+     * Indicates whether the display state is ready to update. If this is the default display, we
+     * want to update it right away so that we can draw the boot animation on it. If it is not
+     * the default display, drawing the boot animation on it would look incorrect, so we need
+     * to wait until boot is completed.
+     * @return True if the display state is ready to update
+     */
+    private boolean readyToUpdateDisplayState() {
+        return mDisplayId == Display.DEFAULT_DISPLAY || mBootCompleted;
+    }
+
     @Override
     public void dump(final PrintWriter pw) {
         synchronized (mLock) {
@@ -3235,6 +3257,11 @@
                 case MSG_SWITCH_USER:
                     handleOnSwitchUser(msg.arg1);
                     break;
+
+                case MSG_BOOT_COMPLETED:
+                    mBootCompleted = true;
+                    updatePowerState();
+                    break;
             }
         }
     }
@@ -3418,6 +3445,23 @@
                     darkeningThresholdLevels, minDarkeningThreshold, minBrighteningThreshold,
                     potentialOldBrightnessRange);
         }
+
+        ScreenOffBrightnessSensorController getScreenOffBrightnessSensorController(
+                SensorManager sensorManager,
+                Sensor lightSensor,
+                Handler handler,
+                ScreenOffBrightnessSensorController.Clock clock,
+                int[] sensorValueToLux,
+                BrightnessMappingStrategy brightnessMapper) {
+            return new ScreenOffBrightnessSensorController(
+                    sensorManager,
+                    lightSensor,
+                    handler,
+                    clock,
+                    sensorValueToLux,
+                    brightnessMapper
+            );
+        }
     }
 
     static class CachedBrightnessInfo {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index 0861a37..a928777 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -137,6 +137,7 @@
     private static final int MSG_BRIGHTNESS_RAMP_DONE = 10;
     private static final int MSG_STATSD_HBM_BRIGHTNESS = 11;
     private static final int MSG_SWITCH_USER = 12;
+    private static final int MSG_BOOT_COMPLETED = 13;
 
     private static final int BRIGHTNESS_CHANGE_STATSD_REPORT_INTERVAL_MS = 500;
 
@@ -425,6 +426,8 @@
     private SparseArray<DisplayPowerControllerInterface> mDisplayBrightnessFollowers =
             new SparseArray();
 
+    private boolean mBootCompleted;
+
     /**
      * Creates the display power controller.
      */
@@ -432,7 +435,8 @@
             DisplayPowerCallbacks callbacks, Handler handler,
             SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay,
             BrightnessTracker brightnessTracker, BrightnessSetting brightnessSetting,
-            Runnable onBrightnessChangeRunnable, HighBrightnessModeMetadata hbmMetadata) {
+            Runnable onBrightnessChangeRunnable, HighBrightnessModeMetadata hbmMetadata,
+            boolean bootCompleted) {
 
         mInjector = injector != null ? injector : new Injector();
         mClock = mInjector.getClock();
@@ -555,6 +559,7 @@
         mTemporaryAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT;
         mPendingAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT;
 
+        mBootCompleted = bootCompleted;
     }
 
     private void applyReduceBrightColorsSplineAdjustment() {
@@ -1025,17 +1030,15 @@
 
             loadScreenOffBrightnessSensor();
             int[] sensorValueToLux = mDisplayDeviceConfig.getScreenOffBrightnessSensorValueToLux();
-            // TODO (b/265793751): Don't instantiate ScreenOffBrightnessSensorController if this is
-            // a complementary display
             if (mScreenOffBrightnessSensor != null && sensorValueToLux != null) {
-                mScreenOffBrightnessSensorController = new ScreenOffBrightnessSensorController(
-                        mSensorManager,
-                        mScreenOffBrightnessSensor,
-                        mHandler,
-                        SystemClock::uptimeMillis,
-                        sensorValueToLux,
-                        mInteractiveModeBrightnessMapper
-                );
+                mScreenOffBrightnessSensorController =
+                        mInjector.getScreenOffBrightnessSensorController(
+                                mSensorManager,
+                                mScreenOffBrightnessSensor,
+                                mHandler,
+                                SystemClock::uptimeMillis,
+                                sensorValueToLux,
+                                mInteractiveModeBrightnessMapper);
             }
         } else {
             mUseSoftwareAutoBrightnessConfig = false;
@@ -1184,13 +1187,14 @@
 
         if (mScreenOffBrightnessSensorController != null) {
             mScreenOffBrightnessSensorController.setLightSensorEnabled(mUseAutoBrightness
-                    && (state == Display.STATE_OFF || (state == Display.STATE_DOZE
-                    && !mDisplayBrightnessController.isAllowAutoBrightnessWhileDozingConfig())));
+                    && mIsEnabled && (state == Display.STATE_OFF || (state == Display.STATE_DOZE
+                    && !mDisplayBrightnessController.isAllowAutoBrightnessWhileDozingConfig()))
+                    && mLeadDisplayId == Layout.NO_LEAD_DISPLAY);
         }
 
         // Initialize things the first time the power state is changed.
         if (mustInitialize) {
-            initialize(state);
+            initialize(readyToUpdateDisplayState() ? state : Display.STATE_UNKNOWN);
         }
 
         // Animate the screen state change unless already animating.
@@ -1720,6 +1724,12 @@
         }
     }
 
+    @Override
+    public void onBootCompleted() {
+        Message msg = mHandler.obtainMessage(MSG_BOOT_COMPLETED);
+        mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
+    }
+
     private boolean saveBrightnessInfo(float brightness) {
         return saveBrightnessInfo(brightness, brightness);
     }
@@ -1861,7 +1871,8 @@
                 }
             }
 
-            if (!reportOnly && mPowerState.getScreenState() != state) {
+            if (!reportOnly && mPowerState.getScreenState() != state
+                    && readyToUpdateDisplayState()) {
                 Trace.traceCounter(Trace.TRACE_TAG_POWER, "ScreenState", state);
                 // TODO(b/153319140) remove when we can get this from the above trace invocation
                 SystemProperties.set("debug.tracing.screen_state", String.valueOf(state));
@@ -2138,11 +2149,11 @@
                 mContext.getContentResolver(),
                 Settings.System.SCREEN_BRIGHTNESS_MODE,
                 Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, UserHandle.USER_CURRENT);
-        mHandler.post(() -> {
+        mHandler.postAtTime(() -> {
             mUseAutoBrightness = screenBrightnessModeSetting
                     == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC;
             updatePowerState();
-        });
+        }, mClock.uptimeMillis());
     }
 
     private float getAutoBrightnessAdjustmentSetting() {
@@ -2519,6 +2530,17 @@
         }
     }
 
+    /**
+     * Indicates whether the display state is ready to update. If this is the default display, we
+     * want to update it right away so that we can draw the boot animation on it. If it is not
+     * the default display, drawing the boot animation on it would look incorrect, so we need
+     * to wait until boot is completed.
+     * @return True if the display state is ready to update
+     */
+    private boolean readyToUpdateDisplayState() {
+        return mDisplayId == Display.DEFAULT_DISPLAY || mBootCompleted;
+    }
+
     // Return bucket index of range_[left]_[right] where
     // left <= nits < right
     private int nitsToRangeIndex(float nits) {
@@ -2738,6 +2760,11 @@
                 case MSG_SWITCH_USER:
                     handleOnSwitchUser(msg.arg1);
                     break;
+
+                case MSG_BOOT_COMPLETED:
+                    mBootCompleted = true;
+                    updatePowerState();
+                    break;
             }
         }
     }
@@ -2896,6 +2923,23 @@
                     darkeningThresholdLevels, minDarkeningThreshold, minBrighteningThreshold,
                     potentialOldBrightnessRange);
         }
+
+        ScreenOffBrightnessSensorController getScreenOffBrightnessSensorController(
+                SensorManager sensorManager,
+                Sensor lightSensor,
+                Handler handler,
+                ScreenOffBrightnessSensorController.Clock clock,
+                int[] sensorValueToLux,
+                BrightnessMappingStrategy brightnessMapper) {
+            return new ScreenOffBrightnessSensorController(
+                    sensorManager,
+                    lightSensor,
+                    handler,
+                    clock,
+                    sensorValueToLux,
+                    brightnessMapper
+            );
+        }
     }
 
     static class CachedBrightnessInfo {
diff --git a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
index 0bc8154..73edb97 100644
--- a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
+++ b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
@@ -206,4 +206,9 @@
      * @param follower The DPC to remove from the followers list
      */
     void removeDisplayBrightnessFollower(DisplayPowerControllerInterface follower);
+
+    /**
+     * Indicate that boot has been completed and the screen is ready to update.
+     */
+    void onBootCompleted();
 }
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index 8f65775..25fd5ea 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -370,11 +370,6 @@
             mLastRestartTimestampMap.put(contextHubId,
                     new AtomicLong(SystemClock.elapsedRealtimeNanos()));
 
-            IContextHubClient client = mClientManager.registerClient(
-                    contextHubInfo, createDefaultClientCallback(contextHubId),
-                    /* attributionTag= */ null, mTransactionManager, mContext.getPackageName());
-            defaultClientMap.put(contextHubId, client);
-
             try {
                 mContextHubWrapper.registerCallback(contextHubId,
                         new ContextHubServiceCallback(contextHubId));
@@ -383,6 +378,11 @@
                         + contextHubId + ")", e);
             }
 
+            IContextHubClient client = mClientManager.registerClient(
+                    contextHubInfo, createDefaultClientCallback(contextHubId),
+                    /* attributionTag= */ null, mTransactionManager, mContext.getPackageName());
+            defaultClientMap.put(contextHubId, client);
+
             // Do a query to initialize the service cache list of nanoapps
             // TODO(b/194289715): Remove this when old API is deprecated
             queryNanoAppsInternal(contextHubId);
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 11efd41..58ae955 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -3938,22 +3938,22 @@
 
     @GuardedBy({"mPackagesLock"})
     private void fallbackToSingleUserLP() {
-        int flags = UserInfo.FLAG_SYSTEM | UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_ADMIN
-                | UserInfo.FLAG_PRIMARY;
         // Create the system user
-        String systemUserType = isDefaultHeadlessSystemUserMode()
+        final String systemUserType = isDefaultHeadlessSystemUserMode()
                 ? UserManager.USER_TYPE_SYSTEM_HEADLESS
                 : UserManager.USER_TYPE_FULL_SYSTEM;
-        flags |= mUserTypes.get(systemUserType).getDefaultUserInfoFlags();
-        UserInfo system = new UserInfo(UserHandle.USER_SYSTEM, null, null, flags, systemUserType);
-        UserData userData = putUserInfo(system);
+        final int flags = mUserTypes.get(systemUserType).getDefaultUserInfoFlags()
+                | UserInfo.FLAG_INITIALIZED;
+        final UserInfo system = new UserInfo(UserHandle.USER_SYSTEM,
+                /* name= */ null, /* iconPath= */ null, flags, systemUserType);
+        final UserData userData = putUserInfo(system);
         userData.userProperties = new UserProperties(
                 mUserTypes.get(userData.info.userType).getDefaultUserPropertiesReference());
         mNextSerialNumber = MIN_USER_ID;
         mUserVersion = USER_VERSION;
         mUserTypeVersion = UserTypeFactory.getUserTypeVersion();
 
-        Bundle restrictions = new Bundle();
+        final Bundle restrictions = new Bundle();
         try {
             final String[] defaultFirstUserRestrictions = mContext.getResources().getStringArray(
                     com.android.internal.R.array.config_defaultFirstUserRestrictions);
diff --git a/services/core/java/com/android/server/pm/UserTypeDetails.java b/services/core/java/com/android/server/pm/UserTypeDetails.java
index f86ee90..6065372 100644
--- a/services/core/java/com/android/server/pm/UserTypeDetails.java
+++ b/services/core/java/com/android/server/pm/UserTypeDetails.java
@@ -76,7 +76,7 @@
     private final @UserInfoFlag int mBaseType;
 
     // TODO(b/143784345): Update doc/name when we clean up UserInfo.
-    /** The {@link UserInfo.UserInfoFlag}s that all users of this type will automatically have. */
+    /** The {@link UserInfoFlag}s to apply by default to newly created users of this type. */
     private final @UserInfoFlag int mDefaultUserInfoPropertyFlags;
 
     /**
@@ -224,7 +224,7 @@
     }
 
     // TODO(b/143784345): Update comment when UserInfo is reorganized.
-    /** The {@link UserInfo.UserInfoFlag}s that all users of this type will automatically have. */
+    /** The {@link UserInfoFlag}s to apply by default to newly created users of this type. */
     public int getDefaultUserInfoFlags() {
         return mDefaultUserInfoPropertyFlags | mBaseType;
     }
@@ -526,6 +526,7 @@
             Preconditions.checkArgument(hasValidPropertyFlags(),
                     "UserTypeDetails " + mName + " has invalid flags: "
                             + Integer.toHexString(mDefaultUserInfoPropertyFlags));
+            checkSystemAndMainUserPreconditions();
             if (hasBadge()) {
                 Preconditions.checkArgument(mBadgeLabels != null && mBadgeLabels.length != 0,
                         "UserTypeDetails " + mName + " has badge but no badgeLabels.");
@@ -578,8 +579,6 @@
         // TODO(b/143784345): Refactor this when we clean up UserInfo.
         private boolean hasValidPropertyFlags() {
             final int forbiddenMask =
-                    UserInfo.FLAG_PRIMARY |
-                    UserInfo.FLAG_ADMIN |
                     UserInfo.FLAG_INITIALIZED |
                     UserInfo.FLAG_QUIET_MODE |
                     UserInfo.FLAG_FULL |
@@ -587,6 +586,18 @@
                     UserInfo.FLAG_PROFILE;
             return (mDefaultUserInfoPropertyFlags & forbiddenMask) == 0;
         }
+
+        private void checkSystemAndMainUserPreconditions() {
+            // Primary must be synonymous with System.
+            Preconditions.checkArgument(
+                    ((mBaseType & UserInfo.FLAG_SYSTEM) != 0) ==
+                            ((mDefaultUserInfoPropertyFlags & UserInfo.FLAG_PRIMARY) != 0),
+                    "UserTypeDetails " + mName + " cannot be SYSTEM xor PRIMARY.");
+            // At most one MainUser is ever allowed at a time.
+            Preconditions.checkArgument(
+                    ((mDefaultUserInfoPropertyFlags & UserInfo.FLAG_MAIN) == 0) || mMaxAllowed == 1,
+                    "UserTypeDetails " + mName + " must not sanction more than one MainUser.");
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index 4cf8c09..b7a2b86 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -16,11 +16,14 @@
 
 package com.android.server.pm;
 
+import static android.content.pm.UserInfo.FLAG_ADMIN;
 import static android.content.pm.UserInfo.FLAG_DEMO;
 import static android.content.pm.UserInfo.FLAG_EPHEMERAL;
 import static android.content.pm.UserInfo.FLAG_FULL;
 import static android.content.pm.UserInfo.FLAG_GUEST;
+import static android.content.pm.UserInfo.FLAG_MAIN;
 import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE;
+import static android.content.pm.UserInfo.FLAG_PRIMARY;
 import static android.content.pm.UserInfo.FLAG_PROFILE;
 import static android.content.pm.UserInfo.FLAG_RESTRICTED;
 import static android.content.pm.UserInfo.FLAG_SYSTEM;
@@ -62,7 +65,7 @@
  * This class is responsible both for defining the AOSP use types, as well as reading in customized
  * user types from {@link com.android.internal.R.xml#config_user_types}.
  *
- * Tests are located in UserManagerServiceUserTypeTest.java.
+ * Tests are located in {@link UserManagerServiceUserTypeTest}.
  * @hide
  */
 public final class UserTypeFactory {
@@ -277,7 +280,8 @@
         return new UserTypeDetails.Builder()
                 .setName(USER_TYPE_FULL_SYSTEM)
                 .setBaseType(FLAG_SYSTEM | FLAG_FULL)
-                .setDefaultUserInfoPropertyFlags(UserInfo.FLAG_MAIN);
+                .setDefaultUserInfoPropertyFlags(FLAG_PRIMARY | FLAG_ADMIN | FLAG_MAIN)
+                .setMaxAllowed(1);
     }
 
     /**
@@ -287,7 +291,9 @@
     private static UserTypeDetails.Builder getDefaultTypeSystemHeadless() {
         return new UserTypeDetails.Builder()
                 .setName(USER_TYPE_SYSTEM_HEADLESS)
-                .setBaseType(FLAG_SYSTEM);
+                .setBaseType(FLAG_SYSTEM)
+                .setDefaultUserInfoPropertyFlags(FLAG_PRIMARY | FLAG_ADMIN)
+                .setMaxAllowed(1);
     }
 
     private static Bundle getDefaultSecondaryUserRestrictions() {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index 78761bd..cc2c9ad 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -3673,9 +3673,9 @@
                 isAppOpPermission = bp.isAppOp();
             }
 
+            final int flags = getPermissionFlagsInternal(pkg.getPackageName(), permission,
+                    myUid, userId);
             if (shouldGrantRuntimePermission) {
-                final int flags = getPermissionFlagsInternal(pkg.getPackageName(), permission,
-                        myUid, userId);
                 if (supportsRuntimePermissions) {
                     // Installer cannot change immutable permissions.
                     if ((flags & immutableFlags) == 0) {
@@ -3693,6 +3693,9 @@
             } else if (isAppOpPermission
                     && PackageInstallerService.INSTALLER_CHANGEABLE_APP_OP_PERMISSIONS
                     .contains(permission)) {
+                if ((flags & PackageManager.FLAG_PERMISSION_USER_SET) != 0) {
+                    continue;
+                }
                 int mode =
                         permissionState == PERMISSION_STATE_GRANTED ? MODE_ALLOWED : MODE_ERRORED;
                 int uid = UserHandle.getUid(userId, pkg.getUid());
diff --git a/services/core/java/com/android/server/timedetector/NetworkTimeUpdateService.java b/services/core/java/com/android/server/timedetector/NetworkTimeUpdateService.java
index 0809297..4a6c794 100644
--- a/services/core/java/com/android/server/timedetector/NetworkTimeUpdateService.java
+++ b/services/core/java/com/android/server/timedetector/NetworkTimeUpdateService.java
@@ -115,7 +115,7 @@
         TimeDetectorInternal timeDetectorInternal =
                 LocalServices.getService(TimeDetectorInternal.class);
         // Broadcast alarms sent by system are immutable
-        Intent pollIntent = new Intent(ACTION_POLL, null);
+        Intent pollIntent = new Intent(ACTION_POLL, null).setPackage("android");
         PendingIntent pendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST,
                 pollIntent, PendingIntent.FLAG_IMMUTABLE);
         mRefreshCallbacks = new Engine.RefreshCallbacks() {
@@ -228,16 +228,14 @@
     }
 
     private void onPollNetworkTime(@NonNull String reason) {
-        // If we don't have any default network, don't bother.
         Network network;
         synchronized (mLock) {
             network = mDefaultNetwork;
         }
-        if (network == null) return;
 
         mWakeLock.acquire();
         try {
-            mEngine.refreshIfRequiredAndReschedule(network, reason, mRefreshCallbacks);
+            mEngine.refreshAndRescheduleIfRequired(network, reason, mRefreshCallbacks);
         } finally {
             mWakeLock.release();
         }
@@ -338,10 +336,10 @@
          * Attempts to refresh the network time if required, i.e. if there isn't a recent-enough
          * network time available. It must also schedule the next call. This is a blocking call.
          *
-         * @param network the network to use
+         * @param network the network to use, or null if no network is available
          * @param reason the reason for the refresh (for logging)
          */
-        void refreshIfRequiredAndReschedule(@NonNull Network network, @NonNull String reason,
+        void refreshAndRescheduleIfRequired(@Nullable Network network, @NonNull String reason,
                 @NonNull RefreshCallbacks refreshCallbacks);
 
         void dump(@NonNull PrintWriter pw);
@@ -391,7 +389,7 @@
         /**
          * Records the time of the last refresh attempt (successful or otherwise) by this service.
          * This is used when scheduling the next refresh attempt. In cases where {@link
-         * #refreshIfRequiredAndReschedule} is called too frequently, this will prevent each call
+         * #refreshAndRescheduleIfRequired} is called too frequently, this will prevent each call
          * resulting in a network request. See also {@link #mShortPollingIntervalMillis}.
          *
          * <p>Time servers are a shared resource and so Android should avoid loading them.
@@ -443,9 +441,19 @@
         }
 
         @Override
-        public void refreshIfRequiredAndReschedule(
-                @NonNull Network network, @NonNull String reason,
+        public void refreshAndRescheduleIfRequired(
+                @Nullable Network network, @NonNull String reason,
                 @NonNull RefreshCallbacks refreshCallbacks) {
+            if (network == null) {
+                // If we don't have any default network, don't do anything: When a new network
+                // is available then this method will be called again.
+                logToDebugAndDumpsys("refreshIfRequiredAndReschedule:"
+                        + " reason=" + reason
+                        + ": No default network available. No refresh attempted and no next"
+                        + " attempt scheduled.");
+                return;
+            }
+
             // Attempt to refresh the network time if there is no latest time result, or if the
             // latest time result is considered too old.
             NtpTrustedTime.TimeResult initialTimeResult = mNtpTrustedTime.getCachedTimeResult();
diff --git a/services/core/java/com/android/server/timedetector/NetworkTimeUpdateServiceShellCommand.java b/services/core/java/com/android/server/timedetector/NetworkTimeUpdateServiceShellCommand.java
index 410b50f..afc0bdd 100644
--- a/services/core/java/com/android/server/timedetector/NetworkTimeUpdateServiceShellCommand.java
+++ b/services/core/java/com/android/server/timedetector/NetworkTimeUpdateServiceShellCommand.java
@@ -157,7 +157,7 @@
                 SET_SERVER_CONFIG_SERVER_ARG, SET_SERVER_CONFIG_SERVER_ARG,
                 SET_SERVER_CONFIG_TIMEOUT_ARG);
         pw.printf("      NTP server URIs must be in the form \"ntp://hostname\" or"
-                + " \"ntp://hostname:port\"");
+                + " \"ntp://hostname:port\"\n");
         pw.printf("  %s\n", SHELL_COMMAND_RESET_SERVER_CONFIG);
         pw.printf("    Resets/clears the NTP server config set via %s.\n",
                 SHELL_COMMAND_SET_SERVER_CONFIG);
diff --git a/services/core/java/com/android/server/timedetector/ServerFlags.java b/services/core/java/com/android/server/timedetector/ServerFlags.java
index 6782229..28d34c2 100644
--- a/services/core/java/com/android/server/timedetector/ServerFlags.java
+++ b/services/core/java/com/android/server/timedetector/ServerFlags.java
@@ -304,7 +304,7 @@
 
     /**
      * Returns an {@link Instant} from {@link DeviceConfig} from the system_time
-     * namespace, returns the {@code defaultValue} if the value is missing or invalid.
+     * namespace, returns {@link Optional#empty()} if there is no explicit value set.
      */
     @NonNull
     public Optional<Instant> getOptionalInstant(@DeviceConfigKey String key) {
@@ -341,16 +341,17 @@
     }
 
     /**
-     * Returns a boolean value from {@link DeviceConfig} from the system_time
-     * namespace, or {@code defaultValue} if there is no explicit value set.
+     * Returns a boolean value from {@link DeviceConfig} from the system_time namespace, or
+     * {@code defaultValue} if there is no explicit value set.
      */
     public boolean getBoolean(@DeviceConfigKey String key, boolean defaultValue) {
         return DeviceConfig.getBoolean(NAMESPACE_SYSTEM_TIME, key, defaultValue);
     }
 
     /**
-     * Returns a positive duration from {@link DeviceConfig} from the system_time
-     * namespace, or {@code defaultValue} if there is no explicit value set.
+     * Returns a positive duration from {@link DeviceConfig} from the system_time namespace,
+     * or {@code defaultValue} if there is no explicit value set or if the value is not a number or
+     * is negative.
      */
     @Nullable
     public Duration getDurationFromMillis(
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index d7829c8..357d3f5 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -772,7 +772,7 @@
                     DisplayConnector connector =
                             targetWallpaper.connection.getDisplayConnectorOrCreate(displayId);
                     if (connector == null) return;
-                    connector.disconnectLocked();
+                    connector.disconnectLocked(targetWallpaper.connection);
                     targetWallpaper.connection.removeDisplayConnector(displayId);
                     mWallpaperDisplayHelper.removeDisplayData(displayId);
                 }
@@ -859,7 +859,7 @@
             if (fallbackConnection.mDisplayConnector.size() != 0) {
                 fallbackConnection.forEachDisplayConnector(connector -> {
                     if (connector.mEngine != null) {
-                        connector.disconnectLocked();
+                        connector.disconnectLocked(fallbackConnection);
                     }
                 });
                 fallbackConnection.mDisplayConnector.clear();
@@ -940,16 +940,14 @@
             t.traceEnd();
         }
 
-        void disconnectLocked() {
+        void disconnectLocked(WallpaperConnection connection) {
             if (DEBUG) Slog.v(TAG, "Removing window token: " + mToken);
             mWindowManagerInternal.removeWindowToken(mToken, false/* removeWindows */,
                     mDisplayId);
             try {
-                if (mEngine != null) {
-                    mEngine.destroy();
-                }
+                connection.mService.detach(mToken);
             } catch (RemoteException e) {
-                Slog.w(TAG, "Engine.destroy() threw a RemoteException");
+                Slog.w(TAG, "connection.mService.destroy() threw a RemoteException");
             }
             mEngine = null;
         }
@@ -1249,12 +1247,7 @@
             synchronized (mLock) {
                 final DisplayConnector connector = getDisplayConnectorOrCreate(displayId);
                 if (connector == null) {
-                    try {
-                        engine.destroy();
-                    } catch (RemoteException e) {
-                        Slog.w(TAG, "Failed to destroy engine", e);
-                    }
-                    return;
+                    throw new IllegalStateException("Connector has already been destroyed");
                 }
                 connector.mEngine = engine;
                 connector.ensureStatusHandled();
@@ -3261,20 +3254,8 @@
                 }
                 wallpaper.connection.mReply = null;
             }
-            try {
-                // It can be null if user switching happens before service connection.
-                if (wallpaper.connection.mService != null) {
-                    wallpaper.connection.mService.detach();
-                }
-            } catch (RemoteException e) {
-                Slog.w(TAG, "Failed detaching wallpaper service ", e);
-            }
-            try {
-                mContext.unbindService(wallpaper.connection);
-            } catch (IllegalArgumentException e) {
-                Slog.w(TAG, "Attempted to unbind unregistered service");
-            }
-            wallpaper.connection.forEachDisplayConnector(DisplayConnector::disconnectLocked);
+            wallpaper.connection.forEachDisplayConnector(
+                    connector -> connector.disconnectLocked(wallpaper.connection));
             wallpaper.connection.mService = null;
             wallpaper.connection.mDisplayConnector.clear();
 
@@ -3284,6 +3265,7 @@
             mContext.getMainThreadHandler().removeCallbacks(
                     wallpaper.connection.mTryToRebindRunnable);
 
+            mContext.unbindService(wallpaper.connection);
             wallpaper.connection = null;
             if (wallpaper == mLastWallpaper) {
                 mLastWallpaper = null;
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 3545747..89b7968 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2159,7 +2159,7 @@
 
         boolean appActivityEmbeddingEnabled = false;
         try {
-            appActivityEmbeddingEnabled = WindowManagerService.sWindowExtensionsEnabled
+            appActivityEmbeddingEnabled = WindowManager.hasWindowExtensionsEnabled()
                     && mAtmService.mContext.getPackageManager()
                             .getProperty(PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED, packageName)
                             .getBoolean();
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index d5bf9f9..c30aef4 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -442,16 +442,6 @@
     static final boolean ENABLE_FIXED_ROTATION_TRANSFORM =
             SystemProperties.getBoolean("persist.wm.fixed_rotation_transform", true);
 
-    /**
-     * Whether the device supports the WindowManager Extensions.
-     * OEMs can enable this by having their device config to inherit window_extensions.mk, such as:
-     * <pre>
-     * $(call inherit-product, $(SRC_TARGET_DIR)/product/window_extensions.mk)
-     * </pre>
-     */
-    static final boolean sWindowExtensionsEnabled =
-            SystemProperties.getBoolean("persist.wm.extensions.enabled", false);
-
     // Enums for animation scale update types.
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({WINDOW_ANIMATION_SCALE, TRANSITION_ANIMATION_SCALE, ANIMATION_DURATION_SCALE})
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index f9592cc..080af7e 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -18,7 +18,7 @@
 
 import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
 import static android.app.ActivityManager.isStartResultSuccessful;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOW_CONFIG_BOUNDS;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.window.TaskFragmentOperation.OP_TYPE_CLEAR_ADJACENT_TASK_FRAGMENTS;
@@ -532,9 +532,9 @@
                 // setWindowingMode call in force-hidden.
                 boolean forceHiddenForPip = false;
                 if (wc.asTask() != null && wc.inPinnedWindowingMode()
-                        && entry.getValue().getWindowingMode() == WINDOWING_MODE_UNDEFINED) {
-                    // We are in pip and going to undefined. Now search hierarchy ops to determine
-                    // whether we are removing pip or expanding pip.
+                        && entry.getValue().getWindowingMode() != WINDOWING_MODE_PINNED) {
+                    // We are going out of pip. Now search hierarchy ops to determine whether we
+                    // are removing pip or expanding pip.
                     for (int i = 0; i < hopSize; ++i) {
                         final WindowContainerTransaction.HierarchyOp hop = hops.get(i);
                         if (hop.getType() != HIERARCHY_OP_TYPE_REORDER) continue;
@@ -670,7 +670,7 @@
                         + " windowing mode during locked task mode.");
             }
 
-            if (windowingMode == WindowConfiguration.WINDOWING_MODE_PINNED) {
+            if (windowingMode == WINDOWING_MODE_PINNED) {
                 // Do not directly put the container into PINNED mode as it may not support it or
                 // the app may not want to enter it. Instead, send a signal to request PIP
                 // mode to the app if they wish to support it below in #applyTaskChanges.
@@ -722,7 +722,7 @@
             tr.mDisplayContent.mPinnedTaskController.setEnterPipBounds(enterPipBounds);
         }
 
-        if (c.getWindowingMode() == WindowConfiguration.WINDOWING_MODE_PINNED
+        if (c.getWindowingMode() == WINDOWING_MODE_PINNED
                 && !tr.inPinnedWindowingMode()) {
             final ActivityRecord activity = tr.getTopNonFinishingActivity();
             if (activity != null) {
diff --git a/services/core/jni/com_android_server_companion_virtual_InputController.cpp b/services/core/jni/com_android_server_companion_virtual_InputController.cpp
index dd757bc..4898d95 100644
--- a/services/core/jni/com_android_server_companion_virtual_InputController.cpp
+++ b/services/core/jni/com_android_server_companion_virtual_InputController.cpp
@@ -22,6 +22,7 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <input/Input.h>
+#include <input/VirtualInputDevice.h>
 #include <linux/uinput.h>
 #include <math.h>
 #include <nativehelper/JNIHelp.h>
@@ -32,16 +33,12 @@
 #include <set>
 #include <string>
 
-/**
- * Log debug messages about native virtual input devices.
- * Enable this via "adb shell setprop log.tag.InputController DEBUG"
- */
-static bool isDebug() {
-    return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO);
-}
+using android::base::unique_fd;
 
 namespace android {
 
+static constexpr jlong INVALID_PTR = 0;
+
 enum class DeviceType {
     KEYBOARD,
     MOUSE,
@@ -49,176 +46,18 @@
     DPAD,
 };
 
-enum class UinputAction {
-    RELEASE = 0,
-    PRESS = 1,
-    MOVE = 2,
-    CANCEL = 3,
-};
-
-static std::map<int, UinputAction> BUTTON_ACTION_MAPPING = {
-        {AMOTION_EVENT_ACTION_BUTTON_PRESS, UinputAction::PRESS},
-        {AMOTION_EVENT_ACTION_BUTTON_RELEASE, UinputAction::RELEASE},
-};
-
-static std::map<int, UinputAction> KEY_ACTION_MAPPING = {
-        {AKEY_EVENT_ACTION_DOWN, UinputAction::PRESS},
-        {AKEY_EVENT_ACTION_UP, UinputAction::RELEASE},
-};
-
-static std::map<int, UinputAction> TOUCH_ACTION_MAPPING = {
-        {AMOTION_EVENT_ACTION_DOWN, UinputAction::PRESS},
-        {AMOTION_EVENT_ACTION_UP, UinputAction::RELEASE},
-        {AMOTION_EVENT_ACTION_MOVE, UinputAction::MOVE},
-        {AMOTION_EVENT_ACTION_CANCEL, UinputAction::CANCEL},
-};
-
-// Button code mapping from https://source.android.com/devices/input/touch-devices
-static std::map<int, int> BUTTON_CODE_MAPPING = {
-        {AMOTION_EVENT_BUTTON_PRIMARY, BTN_LEFT},    {AMOTION_EVENT_BUTTON_SECONDARY, BTN_RIGHT},
-        {AMOTION_EVENT_BUTTON_TERTIARY, BTN_MIDDLE}, {AMOTION_EVENT_BUTTON_BACK, BTN_BACK},
-        {AMOTION_EVENT_BUTTON_FORWARD, BTN_FORWARD},
-};
-
-// Tool type mapping from https://source.android.com/devices/input/touch-devices
-static std::map<int, int> TOOL_TYPE_MAPPING = {
-        {AMOTION_EVENT_TOOL_TYPE_FINGER, MT_TOOL_FINGER},
-        {AMOTION_EVENT_TOOL_TYPE_PALM, MT_TOOL_PALM},
-};
-
-// Dpad keycode mapping from https://source.android.com/devices/input/keyboard-devices
-static std::map<int, int> DPAD_KEY_CODE_MAPPING = {
-        {AKEYCODE_DPAD_DOWN, KEY_DOWN},     {AKEYCODE_DPAD_UP, KEY_UP},
-        {AKEYCODE_DPAD_LEFT, KEY_LEFT},     {AKEYCODE_DPAD_RIGHT, KEY_RIGHT},
-        {AKEYCODE_DPAD_CENTER, KEY_SELECT}, {AKEYCODE_BACK, KEY_BACK},
-};
-
-// Keycode mapping from https://source.android.com/devices/input/keyboard-devices
-static std::map<int, int> KEY_CODE_MAPPING = {
-        {AKEYCODE_0, KEY_0},
-        {AKEYCODE_1, KEY_1},
-        {AKEYCODE_2, KEY_2},
-        {AKEYCODE_3, KEY_3},
-        {AKEYCODE_4, KEY_4},
-        {AKEYCODE_5, KEY_5},
-        {AKEYCODE_6, KEY_6},
-        {AKEYCODE_7, KEY_7},
-        {AKEYCODE_8, KEY_8},
-        {AKEYCODE_9, KEY_9},
-        {AKEYCODE_A, KEY_A},
-        {AKEYCODE_B, KEY_B},
-        {AKEYCODE_C, KEY_C},
-        {AKEYCODE_D, KEY_D},
-        {AKEYCODE_E, KEY_E},
-        {AKEYCODE_F, KEY_F},
-        {AKEYCODE_G, KEY_G},
-        {AKEYCODE_H, KEY_H},
-        {AKEYCODE_I, KEY_I},
-        {AKEYCODE_J, KEY_J},
-        {AKEYCODE_K, KEY_K},
-        {AKEYCODE_L, KEY_L},
-        {AKEYCODE_M, KEY_M},
-        {AKEYCODE_N, KEY_N},
-        {AKEYCODE_O, KEY_O},
-        {AKEYCODE_P, KEY_P},
-        {AKEYCODE_Q, KEY_Q},
-        {AKEYCODE_R, KEY_R},
-        {AKEYCODE_S, KEY_S},
-        {AKEYCODE_T, KEY_T},
-        {AKEYCODE_U, KEY_U},
-        {AKEYCODE_V, KEY_V},
-        {AKEYCODE_W, KEY_W},
-        {AKEYCODE_X, KEY_X},
-        {AKEYCODE_Y, KEY_Y},
-        {AKEYCODE_Z, KEY_Z},
-        {AKEYCODE_GRAVE, KEY_GRAVE},
-        {AKEYCODE_MINUS, KEY_MINUS},
-        {AKEYCODE_EQUALS, KEY_EQUAL},
-        {AKEYCODE_LEFT_BRACKET, KEY_LEFTBRACE},
-        {AKEYCODE_RIGHT_BRACKET, KEY_RIGHTBRACE},
-        {AKEYCODE_BACKSLASH, KEY_BACKSLASH},
-        {AKEYCODE_SEMICOLON, KEY_SEMICOLON},
-        {AKEYCODE_APOSTROPHE, KEY_APOSTROPHE},
-        {AKEYCODE_COMMA, KEY_COMMA},
-        {AKEYCODE_PERIOD, KEY_DOT},
-        {AKEYCODE_SLASH, KEY_SLASH},
-        {AKEYCODE_ALT_LEFT, KEY_LEFTALT},
-        {AKEYCODE_ALT_RIGHT, KEY_RIGHTALT},
-        {AKEYCODE_CTRL_LEFT, KEY_LEFTCTRL},
-        {AKEYCODE_CTRL_RIGHT, KEY_RIGHTCTRL},
-        {AKEYCODE_SHIFT_LEFT, KEY_LEFTSHIFT},
-        {AKEYCODE_SHIFT_RIGHT, KEY_RIGHTSHIFT},
-        {AKEYCODE_META_LEFT, KEY_LEFTMETA},
-        {AKEYCODE_META_RIGHT, KEY_RIGHTMETA},
-        {AKEYCODE_CAPS_LOCK, KEY_CAPSLOCK},
-        {AKEYCODE_SCROLL_LOCK, KEY_SCROLLLOCK},
-        {AKEYCODE_NUM_LOCK, KEY_NUMLOCK},
-        {AKEYCODE_ENTER, KEY_ENTER},
-        {AKEYCODE_TAB, KEY_TAB},
-        {AKEYCODE_SPACE, KEY_SPACE},
-        {AKEYCODE_DPAD_DOWN, KEY_DOWN},
-        {AKEYCODE_DPAD_UP, KEY_UP},
-        {AKEYCODE_DPAD_LEFT, KEY_LEFT},
-        {AKEYCODE_DPAD_RIGHT, KEY_RIGHT},
-        {AKEYCODE_MOVE_END, KEY_END},
-        {AKEYCODE_MOVE_HOME, KEY_HOME},
-        {AKEYCODE_PAGE_DOWN, KEY_PAGEDOWN},
-        {AKEYCODE_PAGE_UP, KEY_PAGEUP},
-        {AKEYCODE_DEL, KEY_BACKSPACE},
-        {AKEYCODE_FORWARD_DEL, KEY_DELETE},
-        {AKEYCODE_INSERT, KEY_INSERT},
-        {AKEYCODE_ESCAPE, KEY_ESC},
-        {AKEYCODE_BREAK, KEY_PAUSE},
-        {AKEYCODE_F1, KEY_F1},
-        {AKEYCODE_F2, KEY_F2},
-        {AKEYCODE_F3, KEY_F3},
-        {AKEYCODE_F4, KEY_F4},
-        {AKEYCODE_F5, KEY_F5},
-        {AKEYCODE_F6, KEY_F6},
-        {AKEYCODE_F7, KEY_F7},
-        {AKEYCODE_F8, KEY_F8},
-        {AKEYCODE_F9, KEY_F9},
-        {AKEYCODE_F10, KEY_F10},
-        {AKEYCODE_F11, KEY_F11},
-        {AKEYCODE_F12, KEY_F12},
-        {AKEYCODE_BACK, KEY_BACK},
-        {AKEYCODE_FORWARD, KEY_FORWARD},
-        {AKEYCODE_NUMPAD_1, KEY_KP1},
-        {AKEYCODE_NUMPAD_2, KEY_KP2},
-        {AKEYCODE_NUMPAD_3, KEY_KP3},
-        {AKEYCODE_NUMPAD_4, KEY_KP4},
-        {AKEYCODE_NUMPAD_5, KEY_KP5},
-        {AKEYCODE_NUMPAD_6, KEY_KP6},
-        {AKEYCODE_NUMPAD_7, KEY_KP7},
-        {AKEYCODE_NUMPAD_8, KEY_KP8},
-        {AKEYCODE_NUMPAD_9, KEY_KP9},
-        {AKEYCODE_NUMPAD_0, KEY_KP0},
-        {AKEYCODE_NUMPAD_ADD, KEY_KPPLUS},
-        {AKEYCODE_NUMPAD_SUBTRACT, KEY_KPMINUS},
-        {AKEYCODE_NUMPAD_MULTIPLY, KEY_KPASTERISK},
-        {AKEYCODE_NUMPAD_DIVIDE, KEY_KPSLASH},
-        {AKEYCODE_NUMPAD_DOT, KEY_KPDOT},
-        {AKEYCODE_NUMPAD_ENTER, KEY_KPENTER},
-        {AKEYCODE_NUMPAD_EQUALS, KEY_KPEQUAL},
-        {AKEYCODE_NUMPAD_COMMA, KEY_KPCOMMA},
-};
-
-/*
- * Map from the uinput touchscreen fd to the pointers present in the previous touch events that
- * hasn't been lifted.
- * We only allow pointer id to go up to MAX_POINTERS because the maximum slots of virtual
- * touchscreen is set up with MAX_POINTERS. Note that in other cases Android allows pointer id to go
- * up to MAX_POINTERS_ID.
- */
-static std::map<int32_t, std::bitset<MAX_POINTERS>> unreleasedTouches;
+static unique_fd invalidFd() {
+    return unique_fd(-1);
+}
 
 /** Creates a new uinput device and assigns a file descriptor. */
-static int openUinput(const char* readableName, jint vendorId, jint productId, const char* phys,
-                      DeviceType deviceType, jint screenHeight, jint screenWidth) {
-    android::base::unique_fd fd(TEMP_FAILURE_RETRY(::open("/dev/uinput", O_WRONLY | O_NONBLOCK)));
+static unique_fd openUinput(const char* readableName, jint vendorId, jint productId,
+                            const char* phys, DeviceType deviceType, jint screenHeight,
+                            jint screenWidth) {
+    unique_fd fd(TEMP_FAILURE_RETRY(::open("/dev/uinput", O_WRONLY | O_NONBLOCK)));
     if (fd < 0) {
         ALOGE("Error creating uinput device: %s", strerror(errno));
-        return -errno;
+        return invalidFd();
     }
 
     ioctl(fd, UI_SET_PHYS, phys);
@@ -227,12 +66,12 @@
     ioctl(fd, UI_SET_EVBIT, EV_SYN);
     switch (deviceType) {
         case DeviceType::DPAD:
-            for (const auto& [_, keyCode] : DPAD_KEY_CODE_MAPPING) {
+            for (const auto& [_, keyCode] : VirtualDpad::DPAD_KEY_CODE_MAPPING) {
                 ioctl(fd, UI_SET_KEYBIT, keyCode);
             }
             break;
         case DeviceType::KEYBOARD:
-            for (const auto& [_, keyCode] : KEY_CODE_MAPPING) {
+            for (const auto& [_, keyCode] : VirtualKeyboard::KEY_CODE_MAPPING) {
                 ioctl(fd, UI_SET_KEYBIT, keyCode);
             }
             break;
@@ -277,7 +116,7 @@
             xAbsSetup.absinfo.minimum = 0;
             if (ioctl(fd, UI_ABS_SETUP, &xAbsSetup) != 0) {
                 ALOGE("Error creating touchscreen uinput x axis: %s", strerror(errno));
-                return -errno;
+                return invalidFd();
             }
             uinput_abs_setup yAbsSetup;
             yAbsSetup.code = ABS_MT_POSITION_Y;
@@ -285,7 +124,7 @@
             yAbsSetup.absinfo.minimum = 0;
             if (ioctl(fd, UI_ABS_SETUP, &yAbsSetup) != 0) {
                 ALOGE("Error creating touchscreen uinput y axis: %s", strerror(errno));
-                return -errno;
+                return invalidFd();
             }
             uinput_abs_setup majorAbsSetup;
             majorAbsSetup.code = ABS_MT_TOUCH_MAJOR;
@@ -293,7 +132,7 @@
             majorAbsSetup.absinfo.minimum = 0;
             if (ioctl(fd, UI_ABS_SETUP, &majorAbsSetup) != 0) {
                 ALOGE("Error creating touchscreen uinput major axis: %s", strerror(errno));
-                return -errno;
+                return invalidFd();
             }
             uinput_abs_setup pressureAbsSetup;
             pressureAbsSetup.code = ABS_MT_PRESSURE;
@@ -301,7 +140,7 @@
             pressureAbsSetup.absinfo.minimum = 0;
             if (ioctl(fd, UI_ABS_SETUP, &pressureAbsSetup) != 0) {
                 ALOGE("Error creating touchscreen uinput pressure axis: %s", strerror(errno));
-                return -errno;
+                return invalidFd();
             }
             uinput_abs_setup slotAbsSetup;
             slotAbsSetup.code = ABS_MT_SLOT;
@@ -309,12 +148,12 @@
             slotAbsSetup.absinfo.minimum = 0;
             if (ioctl(fd, UI_ABS_SETUP, &slotAbsSetup) != 0) {
                 ALOGE("Error creating touchscreen uinput slots: %s", strerror(errno));
-                return -errno;
+                return invalidFd();
             }
         }
         if (ioctl(fd, UI_DEV_SETUP, &setup) != 0) {
             ALOGE("Error creating uinput device: %s", strerror(errno));
-            return -errno;
+            return invalidFd();
         }
     } else {
         // UI_DEV_SETUP was not introduced until version 5. Try setting up manually.
@@ -338,255 +177,118 @@
         }
         if (TEMP_FAILURE_RETRY(write(fd, &fallback, sizeof(fallback))) != sizeof(fallback)) {
             ALOGE("Error creating uinput device: %s", strerror(errno));
-            return -errno;
+            return invalidFd();
         }
     }
 
     if (ioctl(fd, UI_DEV_CREATE) != 0) {
         ALOGE("Error creating uinput device: %s", strerror(errno));
-        return -errno;
+        return invalidFd();
     }
 
-    return fd.release();
+    return fd;
 }
 
-static int openUinputJni(JNIEnv* env, jstring name, jint vendorId, jint productId, jstring phys,
-                         DeviceType deviceType, int screenHeight, int screenWidth) {
+static unique_fd openUinputJni(JNIEnv* env, jstring name, jint vendorId, jint productId,
+                               jstring phys, DeviceType deviceType, int screenHeight,
+                               int screenWidth) {
     ScopedUtfChars readableName(env, name);
     ScopedUtfChars readablePhys(env, phys);
     return openUinput(readableName.c_str(), vendorId, productId, readablePhys.c_str(), deviceType,
                       screenHeight, screenWidth);
 }
 
-static int nativeOpenUinputDpad(JNIEnv* env, jobject thiz, jstring name, jint vendorId,
-                                jint productId, jstring phys) {
-    return openUinputJni(env, name, vendorId, productId, phys, DeviceType::DPAD,
-                         /* screenHeight */ 0, /* screenWidth */ 0);
+static jlong nativeOpenUinputDpad(JNIEnv* env, jobject thiz, jstring name, jint vendorId,
+                                  jint productId, jstring phys) {
+    auto fd = openUinputJni(env, name, vendorId, productId, phys, DeviceType::DPAD,
+                            /* screenHeight= */ 0, /* screenWidth= */ 0);
+    return fd.ok() ? reinterpret_cast<jlong>(new VirtualDpad(std::move(fd))) : INVALID_PTR;
 }
 
-static int nativeOpenUinputKeyboard(JNIEnv* env, jobject thiz, jstring name, jint vendorId,
-                                    jint productId, jstring phys) {
-    return openUinputJni(env, name, vendorId, productId, phys, DeviceType::KEYBOARD,
-                         /* screenHeight */ 0, /* screenWidth */ 0);
+static jlong nativeOpenUinputKeyboard(JNIEnv* env, jobject thiz, jstring name, jint vendorId,
+                                      jint productId, jstring phys) {
+    auto fd = openUinputJni(env, name, vendorId, productId, phys, DeviceType::KEYBOARD,
+                            /* screenHeight= */ 0, /* screenWidth= */ 0);
+    return fd.ok() ? reinterpret_cast<jlong>(new VirtualKeyboard(std::move(fd))) : INVALID_PTR;
 }
 
-static int nativeOpenUinputMouse(JNIEnv* env, jobject thiz, jstring name, jint vendorId,
-                                 jint productId, jstring phys) {
-    return openUinputJni(env, name, vendorId, productId, phys, DeviceType::MOUSE,
-                         /* screenHeight */ 0, /* screenWidth */ 0);
+static jlong nativeOpenUinputMouse(JNIEnv* env, jobject thiz, jstring name, jint vendorId,
+                                   jint productId, jstring phys) {
+    auto fd = openUinputJni(env, name, vendorId, productId, phys, DeviceType::MOUSE,
+                            /* screenHeight= */ 0, /* screenWidth= */ 0);
+    return fd.ok() ? reinterpret_cast<jlong>(new VirtualMouse(std::move(fd))) : INVALID_PTR;
 }
 
-static int nativeOpenUinputTouchscreen(JNIEnv* env, jobject thiz, jstring name, jint vendorId,
-                                       jint productId, jstring phys, jint height, jint width) {
-    return openUinputJni(env, name, vendorId, productId, phys, DeviceType::TOUCHSCREEN, height,
-                         width);
+static jlong nativeOpenUinputTouchscreen(JNIEnv* env, jobject thiz, jstring name, jint vendorId,
+                                         jint productId, jstring phys, jint height, jint width) {
+    auto fd = openUinputJni(env, name, vendorId, productId, phys, DeviceType::TOUCHSCREEN, height,
+                            width);
+    return fd.ok() ? reinterpret_cast<jlong>(new VirtualTouchscreen(std::move(fd))) : INVALID_PTR;
 }
 
-static bool nativeCloseUinput(JNIEnv* env, jobject thiz, jint fd) {
-    ioctl(fd, UI_DEV_DESTROY);
-    if (auto touchesOnFd = unreleasedTouches.find(fd); touchesOnFd != unreleasedTouches.end()) {
-        const size_t remainingPointers = touchesOnFd->second.size();
-        unreleasedTouches.erase(touchesOnFd);
-        ALOGW_IF(remainingPointers > 0, "Closing touchscreen %d, erased %zu unreleased pointers.",
-                 fd, remainingPointers);
-    }
-    return close(fd);
+static void nativeCloseUinput(JNIEnv* env, jobject thiz, jlong ptr) {
+    VirtualInputDevice* virtualInputDevice = reinterpret_cast<VirtualInputDevice*>(ptr);
+    delete virtualInputDevice;
 }
 
-static bool writeInputEvent(int fd, uint16_t type, uint16_t code, int32_t value) {
-    struct input_event ev = {.type = type, .code = code, .value = value};
-    return TEMP_FAILURE_RETRY(write(fd, &ev, sizeof(struct input_event))) == sizeof(ev);
-}
-
-static bool writeKeyEvent(jint fd, jint androidKeyCode, jint action,
-                          const std::map<int, int>& keyCodeMapping) {
-    auto keyCodeIterator = keyCodeMapping.find(androidKeyCode);
-    if (keyCodeIterator == keyCodeMapping.end()) {
-        ALOGE("Unsupported native keycode for androidKeyCode %d", androidKeyCode);
-        return false;
-    }
-    auto actionIterator = KEY_ACTION_MAPPING.find(action);
-    if (actionIterator == KEY_ACTION_MAPPING.end()) {
-        return false;
-    }
-    if (!writeInputEvent(fd, EV_KEY, static_cast<uint16_t>(keyCodeIterator->second),
-                         static_cast<int32_t>(actionIterator->second))) {
-        return false;
-    }
-    if (!writeInputEvent(fd, EV_SYN, SYN_REPORT, 0)) {
-        return false;
-    }
-    return true;
-}
-
-static bool nativeWriteDpadKeyEvent(JNIEnv* env, jobject thiz, jint fd, jint androidKeyCode,
+// Native methods for VirtualDpad
+static bool nativeWriteDpadKeyEvent(JNIEnv* env, jobject thiz, jlong ptr, jint androidKeyCode,
                                     jint action) {
-    return writeKeyEvent(fd, androidKeyCode, action, DPAD_KEY_CODE_MAPPING);
+    VirtualDpad* virtualDpad = reinterpret_cast<VirtualDpad*>(ptr);
+    return virtualDpad->writeDpadKeyEvent(androidKeyCode, action);
 }
 
-static bool nativeWriteKeyEvent(JNIEnv* env, jobject thiz, jint fd, jint androidKeyCode,
+// Native methods for VirtualKeyboard
+static bool nativeWriteKeyEvent(JNIEnv* env, jobject thiz, jlong ptr, jint androidKeyCode,
                                 jint action) {
-    return writeKeyEvent(fd, androidKeyCode, action, KEY_CODE_MAPPING);
+    VirtualKeyboard* virtualKeyboard = reinterpret_cast<VirtualKeyboard*>(ptr);
+    return virtualKeyboard->writeKeyEvent(androidKeyCode, action);
 }
 
-static bool nativeWriteButtonEvent(JNIEnv* env, jobject thiz, jint fd, jint buttonCode,
+// Native methods for VirtualTouchscreen
+static bool nativeWriteTouchEvent(JNIEnv* env, jobject thiz, jlong ptr, jint pointerId,
+                                  jint toolType, jint action, jfloat locationX, jfloat locationY,
+                                  jfloat pressure, jfloat majorAxisSize) {
+    VirtualTouchscreen* virtualTouchscreen = reinterpret_cast<VirtualTouchscreen*>(ptr);
+    return virtualTouchscreen->writeTouchEvent(pointerId, toolType, action, locationX, locationY,
+                                               pressure, majorAxisSize);
+}
+
+// Native methods for VirtualMouse
+static bool nativeWriteButtonEvent(JNIEnv* env, jobject thiz, jlong ptr, jint buttonCode,
                                    jint action) {
-    auto buttonCodeIterator = BUTTON_CODE_MAPPING.find(buttonCode);
-    if (buttonCodeIterator == BUTTON_CODE_MAPPING.end()) {
-        return false;
-    }
-    auto actionIterator = BUTTON_ACTION_MAPPING.find(action);
-    if (actionIterator == BUTTON_ACTION_MAPPING.end()) {
-        return false;
-    }
-    if (!writeInputEvent(fd, EV_KEY, static_cast<uint16_t>(buttonCodeIterator->second),
-                         static_cast<int32_t>(actionIterator->second))) {
-        return false;
-    }
-    if (!writeInputEvent(fd, EV_SYN, SYN_REPORT, 0)) {
-        return false;
-    }
-    return true;
+    VirtualMouse* virtualMouse = reinterpret_cast<VirtualMouse*>(ptr);
+    return virtualMouse->writeButtonEvent(buttonCode, action);
 }
 
-static bool handleTouchUp(int fd, int pointerId) {
-    if (!writeInputEvent(fd, EV_ABS, ABS_MT_TRACKING_ID, static_cast<int32_t>(-1))) {
-        return false;
-    }
-    auto touchesOnFd = unreleasedTouches.find(fd);
-    if (touchesOnFd == unreleasedTouches.end()) {
-        ALOGE("PointerId %d action UP received with no prior events on touchscreen %d.", pointerId,
-              fd);
-        return false;
-    }
-    ALOGD_IF(isDebug(), "Unreleased touches found for touchscreen %d in the map", fd);
-
-    // When a pointer is no longer in touch, remove the pointer id from the corresponding
-    // entry in the unreleased touches map.
-    if (pointerId < 0 || pointerId >= MAX_POINTERS) {
-        ALOGE("Virtual touch event has invalid pointer id %d; value must be between 0 and %zu",
-              pointerId, MAX_POINTERS - 1);
-        return false;
-    }
-    if (!touchesOnFd->second.test(pointerId)) {
-        ALOGE("PointerId %d action UP received with no prior action DOWN on touchscreen %d.",
-              pointerId, fd);
-        return false;
-    }
-    touchesOnFd->second.reset(pointerId);
-    ALOGD_IF(isDebug(), "Pointer %d erased from the touchscreen %d", pointerId, fd);
-
-    // Only sends the BTN UP event when there's no pointers on the touchscreen.
-    if (touchesOnFd->second.none()) {
-        unreleasedTouches.erase(touchesOnFd);
-        if (!writeInputEvent(fd, EV_KEY, BTN_TOUCH, static_cast<int32_t>(UinputAction::RELEASE))) {
-            return false;
-        }
-        ALOGD_IF(isDebug(), "No pointers on touchscreen %d, BTN UP event sent.", fd);
-    }
-    return true;
-}
-
-static bool handleTouchDown(int fd, int pointerId) {
-    // When a new pointer is down on the touchscreen, add the pointer id in the corresponding
-    // entry in the unreleased touches map.
-    auto touchesOnFd = unreleasedTouches.find(fd);
-    if (touchesOnFd == unreleasedTouches.end()) {
-        // Only sends the BTN Down event when the first pointer on the touchscreen is down.
-        if (!writeInputEvent(fd, EV_KEY, BTN_TOUCH, static_cast<int32_t>(UinputAction::PRESS))) {
-            return false;
-        }
-        touchesOnFd = unreleasedTouches.insert({fd, {}}).first;
-        ALOGD_IF(isDebug(), "New touchscreen with fd %d added in the unreleased touches map.", fd);
-    }
-    if (touchesOnFd->second.test(pointerId)) {
-        ALOGE("Repetitive action DOWN event received on a pointer %d that is already down.",
-              pointerId);
-        return false;
-    }
-    touchesOnFd->second.set(pointerId);
-    ALOGD_IF(isDebug(), "Added pointer %d under touchscreen %d in the map", pointerId, fd);
-    if (!writeInputEvent(fd, EV_ABS, ABS_MT_TRACKING_ID, static_cast<int32_t>(pointerId))) {
-        return false;
-    }
-    return true;
-}
-
-static bool nativeWriteTouchEvent(JNIEnv* env, jobject thiz, jint fd, jint pointerId, jint toolType,
-                                  jint action, jfloat locationX, jfloat locationY, jfloat pressure,
-                                  jfloat majorAxisSize) {
-    if (!writeInputEvent(fd, EV_ABS, ABS_MT_SLOT, pointerId)) {
-        return false;
-    }
-    auto toolTypeIterator = TOOL_TYPE_MAPPING.find(toolType);
-    if (toolTypeIterator == TOOL_TYPE_MAPPING.end()) {
-        return false;
-    }
-    if (toolType != -1) {
-        if (!writeInputEvent(fd, EV_ABS, ABS_MT_TOOL_TYPE,
-                             static_cast<int32_t>(toolTypeIterator->second))) {
-            return false;
-        }
-    }
-    auto actionIterator = TOUCH_ACTION_MAPPING.find(action);
-    if (actionIterator == TOUCH_ACTION_MAPPING.end()) {
-        return false;
-    }
-    UinputAction uinputAction = actionIterator->second;
-    if (uinputAction == UinputAction::PRESS && !handleTouchDown(fd, pointerId)) {
-        return false;
-    } else if (uinputAction == UinputAction::RELEASE && !handleTouchUp(fd, pointerId)) {
-        return false;
-    }
-    if (!writeInputEvent(fd, EV_ABS, ABS_MT_POSITION_X, locationX)) {
-        return false;
-    }
-    if (!writeInputEvent(fd, EV_ABS, ABS_MT_POSITION_Y, locationY)) {
-        return false;
-    }
-    if (!isnan(pressure)) {
-        if (!writeInputEvent(fd, EV_ABS, ABS_MT_PRESSURE, pressure)) {
-            return false;
-        }
-    }
-    if (!isnan(majorAxisSize)) {
-        if (!writeInputEvent(fd, EV_ABS, ABS_MT_TOUCH_MAJOR, majorAxisSize)) {
-            return false;
-        }
-    }
-    return writeInputEvent(fd, EV_SYN, SYN_REPORT, 0);
-}
-
-static bool nativeWriteRelativeEvent(JNIEnv* env, jobject thiz, jint fd, jfloat relativeX,
+static bool nativeWriteRelativeEvent(JNIEnv* env, jobject thiz, jlong ptr, jfloat relativeX,
                                      jfloat relativeY) {
-    return writeInputEvent(fd, EV_REL, REL_X, relativeX) &&
-            writeInputEvent(fd, EV_REL, REL_Y, relativeY) &&
-            writeInputEvent(fd, EV_SYN, SYN_REPORT, 0);
+    VirtualMouse* virtualMouse = reinterpret_cast<VirtualMouse*>(ptr);
+    return virtualMouse->writeRelativeEvent(relativeX, relativeY);
 }
 
-static bool nativeWriteScrollEvent(JNIEnv* env, jobject thiz, jint fd, jfloat xAxisMovement,
+static bool nativeWriteScrollEvent(JNIEnv* env, jobject thiz, jlong ptr, jfloat xAxisMovement,
                                    jfloat yAxisMovement) {
-    return writeInputEvent(fd, EV_REL, REL_HWHEEL, xAxisMovement) &&
-            writeInputEvent(fd, EV_REL, REL_WHEEL, yAxisMovement) &&
-            writeInputEvent(fd, EV_SYN, SYN_REPORT, 0);
+    VirtualMouse* virtualMouse = reinterpret_cast<VirtualMouse*>(ptr);
+    return virtualMouse->writeScrollEvent(xAxisMovement, yAxisMovement);
 }
 
 static JNINativeMethod methods[] = {
-        {"nativeOpenUinputDpad", "(Ljava/lang/String;IILjava/lang/String;)I",
+        {"nativeOpenUinputDpad", "(Ljava/lang/String;IILjava/lang/String;)J",
          (void*)nativeOpenUinputDpad},
-        {"nativeOpenUinputKeyboard", "(Ljava/lang/String;IILjava/lang/String;)I",
+        {"nativeOpenUinputKeyboard", "(Ljava/lang/String;IILjava/lang/String;)J",
          (void*)nativeOpenUinputKeyboard},
-        {"nativeOpenUinputMouse", "(Ljava/lang/String;IILjava/lang/String;)I",
+        {"nativeOpenUinputMouse", "(Ljava/lang/String;IILjava/lang/String;)J",
          (void*)nativeOpenUinputMouse},
-        {"nativeOpenUinputTouchscreen", "(Ljava/lang/String;IILjava/lang/String;II)I",
+        {"nativeOpenUinputTouchscreen", "(Ljava/lang/String;IILjava/lang/String;II)J",
          (void*)nativeOpenUinputTouchscreen},
-        {"nativeCloseUinput", "(I)Z", (void*)nativeCloseUinput},
-        {"nativeWriteDpadKeyEvent", "(III)Z", (void*)nativeWriteDpadKeyEvent},
-        {"nativeWriteKeyEvent", "(III)Z", (void*)nativeWriteKeyEvent},
-        {"nativeWriteButtonEvent", "(III)Z", (void*)nativeWriteButtonEvent},
-        {"nativeWriteTouchEvent", "(IIIIFFFF)Z", (void*)nativeWriteTouchEvent},
-        {"nativeWriteRelativeEvent", "(IFF)Z", (void*)nativeWriteRelativeEvent},
-        {"nativeWriteScrollEvent", "(IFF)Z", (void*)nativeWriteScrollEvent},
+        {"nativeCloseUinput", "(J)V", (void*)nativeCloseUinput},
+        {"nativeWriteDpadKeyEvent", "(JII)Z", (void*)nativeWriteDpadKeyEvent},
+        {"nativeWriteKeyEvent", "(JII)Z", (void*)nativeWriteKeyEvent},
+        {"nativeWriteButtonEvent", "(JII)Z", (void*)nativeWriteButtonEvent},
+        {"nativeWriteTouchEvent", "(JIIIFFFF)Z", (void*)nativeWriteTouchEvent},
+        {"nativeWriteRelativeEvent", "(JFF)Z", (void*)nativeWriteRelativeEvent},
+        {"nativeWriteScrollEvent", "(JFF)Z", (void*)nativeWriteScrollEvent},
 };
 
 int register_android_server_companion_virtual_InputController(JNIEnv* env) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
index e16b0f7..c42a457 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
@@ -71,6 +71,10 @@
 import java.util.stream.Collectors;
 
 class ActiveAdmin {
+
+    private final int userId;
+    public final boolean isPermissionBased;
+
     private static final String TAG_DISABLE_KEYGUARD_FEATURES = "disable-keyguard-features";
     private static final String TAG_TEST_ONLY_ADMIN = "test-only-admin";
     private static final String TAG_DISABLE_CAMERA = "disable-camera";
@@ -356,8 +360,20 @@
     String mSmsPackage;
 
     ActiveAdmin(DeviceAdminInfo info, boolean isParent) {
+        this.userId = -1;
         this.info = info;
         this.isParent = isParent;
+        this.isPermissionBased = false;
+    }
+
+    ActiveAdmin(int userId, boolean permissionBased) {
+        if (permissionBased == false) {
+            throw new IllegalArgumentException("Can only pass true for permissionBased admin");
+        }
+        this.userId = userId;
+        this.isPermissionBased = permissionBased;
+        this.isParent = false;
+        this.info = null;
     }
 
     ActiveAdmin getParentActiveAdmin() {
@@ -374,10 +390,16 @@
     }
 
     int getUid() {
+        if (isPermissionBased) {
+            return -1;
+        }
         return info.getActivityInfo().applicationInfo.uid;
     }
 
     public UserHandle getUserHandle() {
+        if (isPermissionBased) {
+            return UserHandle.of(userId);
+        }
         return UserHandle.of(UserHandle.getUserId(info.getActivityInfo().applicationInfo.uid));
     }
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
index a5b9d43..6d51bd7 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
@@ -133,9 +133,9 @@
 
     // Create or get the permission-based admin. The permission-based admin will not have a
     // DeviceAdminInfo or ComponentName.
-    ActiveAdmin createOrGetPermissionBasedAdmin() {
+    ActiveAdmin createOrGetPermissionBasedAdmin(int userId) {
         if (mPermissionBasedAdmin == null) {
-            mPermissionBasedAdmin = new ActiveAdmin(/* info= */ null, /* parent= */ false);
+            mPermissionBasedAdmin = new ActiveAdmin(userId, /* permissionBased= */ true);
         }
         return mPermissionBasedAdmin;
     }
@@ -509,7 +509,7 @@
                         Slogf.w(TAG, e, "Failed loading admin %s", name);
                     }
                 } else if ("permission-based-admin".equals(tag)) {
-                    ActiveAdmin ap = new ActiveAdmin(/* info= */ null, /* parent= */ false);
+                    ActiveAdmin ap = new ActiveAdmin(policy.mUserId, /* permissionBased= */ true);
                     ap.readFromXml(parser, /* overwritePolicies= */ false);
                     policy.mPermissionBasedAdmin = ap;
                 } else if ("delegation".equals(tag)) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 821a5f6..34094e4 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3616,7 +3616,7 @@
             final int N = admins.size();
             for (int i = 0; i < N; i++) {
                 ActiveAdmin admin = admins.get(i);
-                if (admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD)
+                if ((admin.isPermissionBased || admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD))
                         && admin.passwordExpirationTimeout > 0L
                         && now >= admin.passwordExpirationDate - EXPIRATION_GRACE_PERIOD_MS
                         && admin.passwordExpirationDate > 0L) {
@@ -4296,15 +4296,26 @@
         }
     }
 
+    @GuardedBy("getLockObject()")
     private List<ActiveAdmin> getActiveAdminsForLockscreenPoliciesLocked(int userHandle) {
         if (isSeparateProfileChallengeEnabled(userHandle)) {
+
+            if (isPermissionCheckFlagEnabled()) {
+                return getActiveAdminsForAffectedUserInclPermissionBasedAdminLocked(userHandle);
+            }
             // If this user has a separate challenge, only return its restrictions.
             return getUserDataUnchecked(userHandle).mAdminList;
         }
         // If isSeparateProfileChallengeEnabled is false and userHandle points to a managed profile
         // we need to query the parent user who owns the credential.
-        return getActiveAdminsForUserAndItsManagedProfilesLocked(getProfileParentId(userHandle),
-                (user) -> !mLockPatternUtils.isSeparateProfileChallengeEnabled(user.id));
+        if (isPermissionCheckFlagEnabled()) {
+            return getActiveAdminsForUserAndItsManagedProfilesInclPermissionBasedAdminLocked(getProfileParentId(userHandle),
+                    (user) -> !mLockPatternUtils.isSeparateProfileChallengeEnabled(user.id));
+        } else {
+            return getActiveAdminsForUserAndItsManagedProfilesLocked(getProfileParentId(userHandle),
+                    (user) -> !mLockPatternUtils.isSeparateProfileChallengeEnabled(user.id));
+        }
+
     }
 
     /**
@@ -4340,7 +4351,14 @@
     @GuardedBy("getLockObject()")
     private List<ActiveAdmin> getActiveAdminsForAffectedUserInclPermissionBasedAdminLocked(
             int userHandle) {
-        List<ActiveAdmin> list = getActiveAdminsForAffectedUserLocked(userHandle);
+        List<ActiveAdmin> list;
+
+        if (isManagedProfile(userHandle)) {
+            list = getUserDataUnchecked(userHandle).mAdminList;
+        }
+        list = getActiveAdminsForUserAndItsManagedProfilesInclPermissionBasedAdminLocked(userHandle,
+                /* shouldIncludeProfileAdmins */ (user) -> false);
+
         if (getUserData(userHandle).mPermissionBasedAdmin != null) {
             list.add(getUserData(userHandle).mPermissionBasedAdmin);
         }
@@ -4378,6 +4396,44 @@
         return admins;
     }
 
+    /**
+     * Returns the list of admins on the given user, as well as parent admins for each managed
+     * profile associated with the given user. Optionally also include the admin of each managed
+     * profile.
+     * <p> Should not be called on a profile user.
+     */
+    @GuardedBy("getLockObject()")
+    private List<ActiveAdmin> getActiveAdminsForUserAndItsManagedProfilesInclPermissionBasedAdminLocked(int userHandle,
+            Predicate<UserInfo> shouldIncludeProfileAdmins) {
+        ArrayList<ActiveAdmin> admins = new ArrayList<>();
+        mInjector.binderWithCleanCallingIdentity(() -> {
+            for (UserInfo userInfo : mUserManager.getProfiles(userHandle)) {
+                DevicePolicyData policy = getUserDataUnchecked(userInfo.id);
+                if (userInfo.id == userHandle) {
+                    admins.addAll(policy.mAdminList);
+                    if (policy.mPermissionBasedAdmin != null) {
+                        admins.add(policy.mPermissionBasedAdmin);
+                    }
+                } else if (userInfo.isManagedProfile()) {
+                    for (int i = 0; i < policy.mAdminList.size(); i++) {
+                        ActiveAdmin admin = policy.mAdminList.get(i);
+                        if (admin.hasParentActiveAdmin()) {
+                            admins.add(admin.getParentActiveAdmin());
+                        }
+                        if (shouldIncludeProfileAdmins.test(userInfo)) {
+                            admins.add(admin);
+                        }
+                    }
+                    if (policy.mPermissionBasedAdmin != null
+                            && shouldIncludeProfileAdmins.test(userInfo)) {
+                        admins.add(policy.mPermissionBasedAdmin);
+                    }
+                }
+            }
+        });
+        return admins;
+    }
+
     private boolean isSeparateProfileChallengeEnabled(int userHandle) {
         return mInjector.binderWithCleanCallingIdentity(() ->
                 mLockPatternUtils.isSeparateProfileChallengeEnabled(userHandle));
@@ -4697,6 +4753,7 @@
      * Return a single admin's expiration date/time, or the min (soonest) for all admins.
      * Returns 0 if not configured.
      */
+    @GuardedBy("getLockObject()")
     private long getPasswordExpirationLocked(ComponentName who, int userHandle, boolean parent) {
         long timeout = 0L;
 
@@ -5282,11 +5339,12 @@
                     adminPackageName, userId, affectedUserId, complexity);
         }
     }
-
+    @GuardedBy("getLockObject()")
     private int getAggregatedPasswordComplexityLocked(@UserIdInt int userHandle) {
         return getAggregatedPasswordComplexityLocked(userHandle, false);
     }
 
+    @GuardedBy("getLockObject()")
     private int getAggregatedPasswordComplexityLocked(@UserIdInt int userHandle,
             boolean deviceWideOnly) {
         ensureLocked();
@@ -5469,6 +5527,7 @@
      * profile.
      * Returns {@code null} if no participating admin has that policy set.
      */
+    @GuardedBy("getLockObject()")
     private ActiveAdmin getAdminWithMinimumFailedPasswordsForWipeLocked(
             int userHandle, boolean parent) {
         int count = 0;
@@ -5699,6 +5758,7 @@
         }
     }
 
+    @GuardedBy("getLockObject()")
     private void updateMaximumTimeToLockLocked(@UserIdInt int userId) {
         // Update the profile's timeout
         if (isManagedProfile(userId)) {
@@ -5727,6 +5787,7 @@
         });
     }
 
+    @GuardedBy("getLockObject()")
     private void updateProfileLockTimeoutLocked(@UserIdInt int userId) {
         final long timeMs;
         if (isSeparateProfileChallengeEnabled(userId)) {
@@ -7459,9 +7520,15 @@
         final String adminName;
         final ComponentName adminComp;
         if (admin != null) {
-            adminComp = admin.info.getComponent();
-            adminName = adminComp.flattenToShortString();
-            event.setAdmin(adminComp);
+            if (admin.isPermissionBased) {
+                adminComp = null;
+                adminName = caller.getPackageName();
+                event.setAdmin(adminName);
+            } else {
+                adminComp = admin.info.getComponent();
+                adminName = adminComp.flattenToShortString();
+                event.setAdmin(adminComp);
+            }
         } else {
             adminComp = null;
             adminName = mInjector.getPackageManager().getPackagesForUid(caller.getUid())[0];
@@ -7750,13 +7817,7 @@
                                 || hasCallingPermission(permission.MASTER_CLEAR)
                                 || hasCallingPermission(MANAGE_DEVICE_POLICY_FACTORY_RESET),
                         "Must be called by the FRP management agent on device");
-                // TODO(b/261999445): Remove
-                if (isHeadlessFlagEnabled()) {
-                    admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked();
-                } else {
-                    admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
-                            UserHandle.getUserId(frpManagementAgentUid));
-                }
+                admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceOrSystemPermissionBasedAdminLocked();
             } else {
                 Preconditions.checkCallAuthorization(
                         isDefaultDeviceOwner(caller)
@@ -7927,12 +7988,13 @@
      *
      * @return the set of user IDs that have been affected
      */
+    @GuardedBy("getLockObject()")
     private Set<Integer> updatePasswordExpirationsLocked(int userHandle) {
         final ArraySet<Integer> affectedUserIds = new ArraySet<>();
         List<ActiveAdmin> admins = getActiveAdminsForLockscreenPoliciesLocked(userHandle);
         for (int i = 0; i < admins.size(); i++) {
             ActiveAdmin admin = admins.get(i);
-            if (admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD)) {
+            if (admin.isPermissionBased || admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD)) {
                 affectedUserIds.add(admin.getUserHandle().getIdentifier());
                 long timeout = admin.passwordExpirationTimeout;
                 admin.passwordExpirationDate =
@@ -8026,6 +8088,9 @@
      */
     private int getUserIdToWipeForFailedPasswords(ActiveAdmin admin) {
         final int userId = admin.getUserHandle().getIdentifier();
+        if (admin.isPermissionBased) {
+            return userId;
+        }
         final ComponentName component = admin.info.getComponent();
         return isProfileOwnerOfOrganizationOwnedDevice(component, userId)
                 ? getProfileParentId(userId) : userId;
@@ -9653,6 +9718,15 @@
         return admin;
     }
 
+    ActiveAdmin getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceOrSystemPermissionBasedAdminLocked() {
+        ensureLocked();
+        ActiveAdmin doOrPo = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked();
+        if (isPermissionCheckFlagEnabled() && doOrPo == null) {
+            return getUserData(0).mPermissionBasedAdmin;
+        }
+        return doOrPo;
+    }
+
     ActiveAdmin getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceParentLocked(int userId) {
         ensureLocked();
         ActiveAdmin admin = getDeviceOwnerAdminLocked();
@@ -10659,9 +10733,12 @@
             return false;
         }
 
-        final ComponentName profileOwner = getProfileOwnerAsUser(userId);
-        if (profileOwner == null) {
-            return false;
+        if (!isPermissionCheckFlagEnabled()) {
+            // TODO: Figure out if something like this needs to be restored for policy engine
+            final ComponentName profileOwner = getProfileOwnerAsUser(userId);
+            if (profileOwner == null) {
+                return false;
+            }
         }
 
         // Managed profiles are not allowed to use lock task
@@ -11884,7 +11961,7 @@
         synchronized (getLockObject()) {
             List<String> result = null;
             // Only device or profile owners can have permitted lists set.
-            List<ActiveAdmin> admins = getActiveAdminsForAffectedUserLocked(userId);
+            List<ActiveAdmin> admins = getActiveAdminsForAffectedUserInclPermissionBasedAdminLocked(userId);
             for (ActiveAdmin admin: admins) {
                 List<String> fromAdmin = admin.permittedInputMethods;
                 if (fromAdmin != null) {
@@ -13623,7 +13700,6 @@
                     new BooleanPolicyValue(uninstallBlocked),
                     caller.getUserId());
         } else {
-            Objects.requireNonNull(who, "ComponentName is null");
             Preconditions.checkCallAuthorization((caller.hasAdminComponent()
                     && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)
                     || isFinancedDeviceOwner(caller)))
@@ -14265,7 +14341,7 @@
 
         final int userId = mInjector.userHandleGetCallingUserId();
         // Is it ok to just check that no active policies exist currently?
-        if (mDevicePolicyEngine.hasActivePolicies()) {
+        if (isDevicePolicyEngineFlagEnabled() && mDevicePolicyEngine.hasActivePolicies()) {
             LockTaskPolicy policy = mDevicePolicyEngine.getResolvedPolicy(
                     PolicyDefinition.LOCK_TASK, userId);
             if (policy == null) {
@@ -15814,7 +15890,7 @@
         if (admin.mPasswordPolicy.quality < minPasswordQuality) {
             return false;
         }
-        return admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
+        return admin.isPermissionBased || admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
     }
 
     @Override
@@ -21451,13 +21527,7 @@
         }
         synchronized (getLockObject()) {
             ActiveAdmin admin;
-            // TODO(b/261999445): remove
-            if (isHeadlessFlagEnabled()) {
-                admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked();
-            } else {
-                admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
-                        UserHandle.USER_SYSTEM);
-            }
+            admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceOrSystemPermissionBasedAdminLocked();
             return admin != null ? admin.mWifiSsidPolicy : null;
         }
     }
@@ -22454,7 +22524,7 @@
             return EnforcingAdmin.createDeviceAdminEnforcingAdmin(who, userId, admin);
         }
         if (admin == null) {
-            admin = getUserData(userId).createOrGetPermissionBasedAdmin();
+            admin = getUserData(userId).createOrGetPermissionBasedAdmin(userId);
         }
         return  EnforcingAdmin.createEnforcingAdmin(caller.getPackageName(), userId, admin);
     }
@@ -23014,26 +23084,12 @@
         return admins;
     }
 
-    // TODO: This can actually accept an EnforcingAdmin that gets created in the permission check
-    //  method.
     private boolean useDevicePolicyEngine(CallerIdentity caller, @Nullable String delegateScope) {
-        if (!isCallerActiveAdminOrDelegate(caller, delegateScope)) {
-            if (!isDevicePolicyEngineFlagEnabled()) {
-                throw new IllegalStateException("Non DPC caller can't set device policies.");
-            }
-            if (hasDPCsNotSupportingCoexistence()) {
-                throw new IllegalStateException("Non DPC caller can't set device policies with "
-                        + "existing legacy admins on the device.");
-            }
-            return true;
-        } else {
-            return isDevicePolicyEngineEnabled();
-        }
+        return isDevicePolicyEngineEnabled();
     }
 
     private boolean isDevicePolicyEngineEnabled() {
-        return isDevicePolicyEngineFlagEnabled() && !hasDPCsNotSupportingCoexistence()
-                && isPermissionCheckFlagEnabled();
+        return isDevicePolicyEngineFlagEnabled() && isPermissionCheckFlagEnabled();
     }
 
     private boolean isDevicePolicyEngineFlagEnabled() {
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
index 68cfe45..dcdee37 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
@@ -16,6 +16,10 @@
 
 package com.android.server.am;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED;
+import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_COLD;
+import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__MANIFEST;
 import static com.android.server.am.BroadcastProcessQueue.REASON_CONTAINS_ALARM;
 import static com.android.server.am.BroadcastProcessQueue.REASON_CONTAINS_FOREGROUND;
 import static com.android.server.am.BroadcastProcessQueue.REASON_CONTAINS_INTERACTIVE;
@@ -44,8 +48,12 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
 
 import android.annotation.NonNull;
 import android.app.Activity;
@@ -70,6 +78,11 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
+import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.ExtendedMockitoTestCase;
+import com.android.server.am.BroadcastQueueTest.SyncBarrier;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -86,7 +99,7 @@
 
 @SmallTest
 @RunWith(MockitoJUnitRunner.class)
-public class BroadcastQueueModernImplTest {
+public class BroadcastQueueModernImplTest extends ExtendedMockitoTestCase {
     private static final int TEST_UID = android.os.Process.FIRST_APPLICATION_UID;
     private static final int TEST_UID2 = android.os.Process.FIRST_APPLICATION_UID + 1;
 
@@ -105,6 +118,11 @@
 
     BroadcastProcessQueue mHead;
 
+    @Override
+    protected void initializeSession(StaticMockitoSessionBuilder builder) {
+        builder.spyStatic(FrameworkStatsLog.class);
+    }
+
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
@@ -1081,6 +1099,28 @@
         }
     }
 
+    @Test
+    public void testBroadcastDeliveryEventReported() throws Exception {
+        final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK);
+        final BroadcastOptions optionsTimeTick = BroadcastOptions.makeBasic();
+        optionsTimeTick.setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT);
+
+        // Halt all processing so that we get a consistent view
+        try (SyncBarrier b = new SyncBarrier(mHandlerThread)) {
+            mImpl.enqueueBroadcastLocked(makeBroadcastRecord(timeTick, optionsTimeTick));
+            mImpl.enqueueBroadcastLocked(makeBroadcastRecord(timeTick, optionsTimeTick));
+        }
+        mImpl.waitForIdle(null);
+
+        // Verify that there is only one delivery event reported since one of the broadcasts
+        // should have been skipped.
+        verify(() -> FrameworkStatsLog.write(eq(BROADCAST_DELIVERY_EVENT_REPORTED),
+                eq(getUidForPackage(PACKAGE_GREEN)), anyInt(), eq(Intent.ACTION_TIME_TICK),
+                eq(BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__MANIFEST),
+                eq(BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_COLD),
+                anyLong(), anyLong(), anyLong(), anyInt()), times(1));
+    }
+
     private Intent createPackageChangedIntent(int uid, List<String> componentNameList) {
         final Intent packageChangedIntent = new Intent(Intent.ACTION_PACKAGE_CHANGED);
         packageChangedIntent.putExtra(Intent.EXTRA_UID, uid);
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 e7b3dd9..f3c2012 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -346,16 +346,18 @@
      * Helper that leverages try-with-resources to pause dispatch of
      * {@link #mHandlerThread} until released.
      */
-    private class SyncBarrier implements AutoCloseable {
+    static class SyncBarrier implements AutoCloseable {
         private final int mToken;
+        private HandlerThread mThread;
 
-        public SyncBarrier() {
-            mToken = mHandlerThread.getLooper().getQueue().postSyncBarrier();
+        SyncBarrier(HandlerThread thread) {
+            mThread = thread;
+            mToken = mThread.getLooper().getQueue().postSyncBarrier();
         }
 
         @Override
         public void close() throws Exception {
-            mHandlerThread.getLooper().getQueue().removeSyncBarrier(mToken);
+            mThread.getLooper().getQueue().removeSyncBarrier(mToken);
         }
     }
 
@@ -1120,7 +1122,7 @@
         final ProcessRecord receiverApp = makeActiveProcessRecord(PACKAGE_GREEN);
 
         final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
-        try (SyncBarrier b = new SyncBarrier()) {
+        try (SyncBarrier b = new SyncBarrier(mHandlerThread)) {
             enqueueBroadcast(makeBroadcastRecord(airplane, callerApp, new ArrayList<>(
                     List.of(makeRegisteredReceiver(receiverApp),
                             makeManifestReceiver(PACKAGE_GREEN, CLASS_RED),
@@ -1164,7 +1166,7 @@
 
         final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
         final Intent timeZone = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
-        try (SyncBarrier b = new SyncBarrier()) {
+        try (SyncBarrier b = new SyncBarrier(mHandlerThread)) {
             enqueueBroadcast(makeBroadcastRecord(airplane, callerApp, USER_GUEST, new ArrayList<>(
                     List.of(makeRegisteredReceiver(callerApp),
                             makeManifestReceiver(PACKAGE_GREEN, CLASS_RED, USER_GUEST),
@@ -1204,7 +1206,7 @@
         final ProcessRecord oldApp = makeActiveProcessRecord(PACKAGE_GREEN);
 
         final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
-        try (SyncBarrier b = new SyncBarrier()) {
+        try (SyncBarrier b = new SyncBarrier(mHandlerThread)) {
             enqueueBroadcast(makeBroadcastRecord(airplane, callerApp, new ArrayList<>(
                     List.of(makeRegisteredReceiver(oldApp),
                             makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN)))));
@@ -1585,7 +1587,7 @@
         final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
         final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
         airplane.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-        try (SyncBarrier b = new SyncBarrier()) {
+        try (SyncBarrier b = new SyncBarrier(mHandlerThread)) {
             enqueueBroadcast(makeBroadcastRecord(timezone, callerApp,
                     List.of(makeRegisteredReceiver(receiverBlueApp, 10),
                             makeRegisteredReceiver(receiverGreenApp, 10),
@@ -1638,7 +1640,7 @@
         final IIntentReceiver resultToFirst = mock(IIntentReceiver.class);
         final IIntentReceiver resultToSecond = mock(IIntentReceiver.class);
 
-        try (SyncBarrier b = new SyncBarrier()) {
+        try (SyncBarrier b = new SyncBarrier(mHandlerThread)) {
             enqueueBroadcast(makeOrderedBroadcastRecord(timezoneFirst, callerApp,
                     List.of(makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE),
                             makeManifestReceiver(PACKAGE_BLUE, CLASS_GREEN)),
@@ -1729,7 +1731,7 @@
         timeTickFirst.putExtra(Intent.EXTRA_INDEX, "third");
         timeTickThird.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
 
-        try (SyncBarrier b = new SyncBarrier()) {
+        try (SyncBarrier b = new SyncBarrier(mHandlerThread)) {
             enqueueBroadcast(makeBroadcastRecord(timeTickFirst, callerApp,
                     List.of(makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE))));
             enqueueBroadcast(makeBroadcastRecord(timeTickSecond, callerApp,
@@ -1771,7 +1773,7 @@
         assertTrue(mQueue.isIdleLocked());
         assertTrue(mQueue.isBeyondBarrierLocked(beforeFirst));
 
-        try (SyncBarrier b = new SyncBarrier()) {
+        try (SyncBarrier b = new SyncBarrier(mHandlerThread)) {
             final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
             enqueueBroadcast(makeBroadcastRecord(timezone, callerApp,
                     List.of(makeRegisteredReceiver(receiverApp))));
@@ -1865,7 +1867,7 @@
         final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE);
 
         final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
-        try (SyncBarrier b = new SyncBarrier()) {
+        try (SyncBarrier b = new SyncBarrier(mHandlerThread)) {
             final Object greenReceiver = makeRegisteredReceiver(receiverGreenApp);
             final Object blueReceiver = makeRegisteredReceiver(receiverBlueApp);
             final Object yellowReceiver = makeManifestReceiver(PACKAGE_YELLOW, CLASS_YELLOW);
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index dc7f3cd..485ce33 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -143,8 +143,8 @@
     private static final String MOCKAPP5_PROCESSNAME = "test #5";
     private static final String MOCKAPP5_PACKAGENAME = "com.android.test.test5";
     private static final int MOCKAPP2_UID_OTHER = MOCKAPP2_UID + UserHandle.PER_USER_RANGE;
-    private static final int FIRST_CACHED_ADJ = ProcessList.CACHED_APP_MIN_ADJ
-            + ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
+    private static int sFirstCachedAdj = ProcessList.CACHED_APP_MIN_ADJ
+                                          + ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
     private static Context sContext;
     private static PackageManagerInternal sPackageManagerInternal;
     private static ActivityManagerService sService;
@@ -208,6 +208,9 @@
                 new ActiveUids(sService, false));
         sService.mOomAdjuster.mAdjSeq = 10000;
         sService.mWakefulness = new AtomicInteger(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        if (sService.mConstants.USE_TIERED_CACHED_ADJ) {
+            sFirstCachedAdj = ProcessList.CACHED_APP_MIN_ADJ + 10;
+        }
     }
 
     @AfterClass
@@ -834,7 +837,7 @@
         updateOomAdj(client, app);
         doReturn(null).when(sService).getTopApp();
 
-        assertProcStates(app, PROCESS_STATE_SERVICE, FIRST_CACHED_ADJ, SCHED_GROUP_BACKGROUND);
+        assertProcStates(app, PROCESS_STATE_SERVICE, sFirstCachedAdj, SCHED_GROUP_BACKGROUND);
     }
 
     @SuppressWarnings("GuardedBy")
@@ -882,7 +885,7 @@
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
-        assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, FIRST_CACHED_ADJ, SCHED_GROUP_BACKGROUND);
+        assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND);
     }
 
     @SuppressWarnings("GuardedBy")
@@ -1286,7 +1289,7 @@
         bindProvider(app, app, null, null, false);
         updateOomAdj(app);
 
-        assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, FIRST_CACHED_ADJ, SCHED_GROUP_BACKGROUND);
+        assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND);
     }
 
     @SuppressWarnings("GuardedBy")
@@ -1301,7 +1304,7 @@
         sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app, client);
 
-        assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, FIRST_CACHED_ADJ, SCHED_GROUP_BACKGROUND);
+        assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND);
     }
 
     @SuppressWarnings("GuardedBy")
@@ -2378,8 +2381,12 @@
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         final int userOwner = 0;
         final int userOther = 1;
-        final int cachedAdj1 = CACHED_APP_MIN_ADJ + ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
-        final int cachedAdj2 = cachedAdj1 + ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2;
+        final int cachedAdj1 = sService.mConstants.USE_TIERED_CACHED_ADJ
+                               ? CACHED_APP_MIN_ADJ + 10
+                               : CACHED_APP_MIN_ADJ + ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
+        final int cachedAdj2 = sService.mConstants.USE_TIERED_CACHED_ADJ
+                               ? CACHED_APP_MIN_ADJ + 10
+                               : cachedAdj1 + ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2;
         doReturn(userOwner).when(sService.mUserController).getCurrentUserId();
 
         final ArrayList<ProcessRecord> lru = sService.mProcessList.getLruProcessesLOSP();
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
index 6e63315..10cfe86 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
@@ -26,12 +26,15 @@
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.isA;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.when;
 
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.res.Resources;
@@ -44,7 +47,9 @@
 import android.os.Looper;
 import android.os.PowerManager;
 import android.os.SystemProperties;
+import android.os.UserHandle;
 import android.os.test.TestLooper;
+import android.provider.Settings;
 import android.util.FloatProperty;
 import android.view.Display;
 import android.view.DisplayInfo;
@@ -57,6 +62,7 @@
 import com.android.server.LocalServices;
 import com.android.server.am.BatteryStatsService;
 import com.android.server.display.RampAnimator.DualRampAnimator;
+import com.android.server.display.brightness.BrightnessEvent;
 import com.android.server.display.color.ColorDisplayService;
 import com.android.server.display.whitebalance.DisplayWhiteBalanceController;
 import com.android.server.policy.WindowManagerPolicy;
@@ -122,6 +128,7 @@
                 .spyStatic(SystemProperties.class)
                 .spyStatic(LocalServices.class)
                 .spyStatic(BatteryStatsService.class)
+                .spyStatic(Settings.System.class)
                 .startMocking();
         mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
         mClock = new OffsettableClock.Stopped();
@@ -139,8 +146,7 @@
                 ColorDisplayService.ColorDisplayServiceInternal.class));
         doAnswer((Answer<Void>) invocationOnMock -> null).when(BatteryStatsService::getService);
 
-        mProxSensor = setUpProxSensor();
-
+        setUpSensors();
         mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
     }
 
@@ -169,7 +175,7 @@
         advanceTime(1);
 
         // two times, one for unfinished business and one for proximity
-        verify(mHolder.wakelockController).acquireWakelock(
+        verify(mHolder.wakelockController, times(2)).acquireWakelock(
                 WakelockController.WAKE_LOCK_UNFINISHED_BUSINESS);
         verify(mHolder.wakelockController).acquireWakelock(
                 WakelockController.WAKE_LOCK_PROXIMITY_DEBOUNCE);
@@ -177,7 +183,7 @@
         mHolder.dpc.stop();
         advanceTime(1);
         // two times, one for unfinished business and one for proximity
-        verify(mHolder.wakelockController).acquireWakelock(
+        verify(mHolder.wakelockController, times(2)).acquireWakelock(
                 WakelockController.WAKE_LOCK_UNFINISHED_BUSINESS);
         verify(mHolder.wakelockController).acquireWakelock(
                 WakelockController.WAKE_LOCK_PROXIMITY_DEBOUNCE);
@@ -214,12 +220,13 @@
         mTestLooper.dispatchAll();
     }
 
-    private Sensor setUpProxSensor() throws Exception {
-        Sensor proxSensor = TestUtils.createSensor(
+    private void setUpSensors() throws Exception {
+        mProxSensor = TestUtils.createSensor(
                 Sensor.TYPE_PROXIMITY, Sensor.STRING_TYPE_PROXIMITY);
+        Sensor screenOffBrightnessSensor = TestUtils.createSensor(
+                Sensor.TYPE_LIGHT, Sensor.STRING_TYPE_LIGHT);
         when(mSensorManagerMock.getSensorList(eq(Sensor.TYPE_ALL)))
-                .thenReturn(List.of(proxSensor));
-        return proxSensor;
+                .thenReturn(List.of(mProxSensor, screenOffBrightnessSensor));
     }
 
     private SensorEventListener getSensorEventListener(Sensor sensor) {
@@ -229,14 +236,15 @@
     }
 
     private void setUpDisplay(int displayId, String uniqueId, LogicalDisplay logicalDisplayMock,
-            DisplayDevice displayDeviceMock, DisplayDeviceConfig displayDeviceConfigMock) {
+            DisplayDevice displayDeviceMock, DisplayDeviceConfig displayDeviceConfigMock,
+            boolean isEnabled) {
         DisplayInfo info = new DisplayInfo();
         DisplayDeviceInfo deviceInfo = new DisplayDeviceInfo();
 
         when(logicalDisplayMock.getDisplayIdLocked()).thenReturn(displayId);
         when(logicalDisplayMock.getPrimaryDisplayDeviceLocked()).thenReturn(displayDeviceMock);
         when(logicalDisplayMock.getDisplayInfoLocked()).thenReturn(info);
-        when(logicalDisplayMock.isEnabledLocked()).thenReturn(true);
+        when(logicalDisplayMock.isEnabledLocked()).thenReturn(isEnabled);
         when(logicalDisplayMock.isInTransitionLocked()).thenReturn(false);
         when(displayDeviceMock.getDisplayDeviceInfoLocked()).thenReturn(deviceInfo);
         when(displayDeviceMock.getUniqueId()).thenReturn(uniqueId);
@@ -253,7 +261,14 @@
         when(displayDeviceConfigMock.getAmbientLightSensor()).thenReturn(
                 new DisplayDeviceConfig.SensorData());
         when(displayDeviceConfigMock.getScreenOffBrightnessSensor()).thenReturn(
-                new DisplayDeviceConfig.SensorData());
+                new DisplayDeviceConfig.SensorData() {
+                    {
+                        type = Sensor.STRING_TYPE_LIGHT;
+                        name = null;
+                    }
+                });
+        when(displayDeviceConfigMock.getScreenOffBrightnessSensorValueToLux())
+                .thenReturn(new int[0]);
     }
 
     @Test
@@ -460,8 +475,164 @@
         verify(secondFollowerDpc.animator).animateTo(eq(brightness), anyFloat(), anyFloat());
     }
 
+    @Test
+    public void testDoesNotSetScreenStateForNonDefaultDisplayUntilBootCompleted() {
+        // We should still set screen state for the default display
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1);
+        verify(mHolder.displayPowerState, times(2)).setScreenState(anyInt());
+
+        mHolder = createDisplayPowerController(42, UNIQUE_ID);
+
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1);
+        verify(mHolder.displayPowerState, never()).setScreenState(anyInt());
+
+        mHolder.dpc.onBootCompleted();
+        advanceTime(1);
+        verify(mHolder.displayPowerState).setScreenState(anyInt());
+    }
+
+    @Test
+    public void testSetScreenOffBrightnessSensorEnabled_DisplayIsOff() {
+        doAnswer((Answer<Integer>) invocationOnMock ->
+                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC)
+                .when(() -> Settings.System.getIntForUser(any(ContentResolver.class),
+                        eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
+                        eq(UserHandle.USER_CURRENT)));
+
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        dpr.policy = DisplayPowerRequest.POLICY_OFF;
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1); // Run updatePowerState
+
+        verify(mHolder.screenOffBrightnessSensorController, atLeastOnce())
+                .setLightSensorEnabled(true);
+
+        // The display turns on and we use the brightness value recommended by
+        // ScreenOffBrightnessSensorController
+        clearInvocations(mHolder.screenOffBrightnessSensorController);
+        float brightness = 0.14f;
+        when(mHolder.screenOffBrightnessSensorController.getAutomaticScreenBrightness())
+                .thenReturn(brightness);
+        dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
+        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
+        when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness(
+                any(BrightnessEvent.class))).thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1); // Run updatePowerState
+
+        verify(mHolder.screenOffBrightnessSensorController, atLeastOnce())
+                .getAutomaticScreenBrightness();
+        verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat());
+    }
+
+    @Test
+    public void testSetScreenOffBrightnessSensorEnabled_DisplayIsInDoze() {
+        doAnswer((Answer<Integer>) invocationOnMock ->
+                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC)
+                .when(() -> Settings.System.getIntForUser(any(ContentResolver.class),
+                        eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
+                        eq(UserHandle.USER_CURRENT)));
+
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        dpr.policy = DisplayPowerRequest.POLICY_DOZE;
+        when(mResourcesMock.getBoolean(
+                com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing))
+                .thenReturn(true);
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1); // Run updatePowerState
+
+        verify(mHolder.screenOffBrightnessSensorController, atLeastOnce())
+                .setLightSensorEnabled(true);
+
+        // The display turns on and we use the brightness value recommended by
+        // ScreenOffBrightnessSensorController
+        clearInvocations(mHolder.screenOffBrightnessSensorController);
+        float brightness = 0.14f;
+        when(mHolder.screenOffBrightnessSensorController.getAutomaticScreenBrightness())
+                .thenReturn(brightness);
+        dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
+        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
+        when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness(
+                any(BrightnessEvent.class))).thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1); // Run updatePowerState
+
+        verify(mHolder.screenOffBrightnessSensorController, atLeastOnce())
+                .getAutomaticScreenBrightness();
+        verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat());
+    }
+
+    @Test
+    public void testSetScreenOffBrightnessSensorDisabled_AutoBrightnessIsDisabled() {
+        doAnswer((Answer<Integer>) invocationOnMock ->
+                Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL)
+                .when(() -> Settings.System.getIntForUser(any(ContentResolver.class),
+                        eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
+                        eq(UserHandle.USER_CURRENT)));
+
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        dpr.policy = DisplayPowerRequest.POLICY_OFF;
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1); // Run updatePowerState
+
+        verify(mHolder.screenOffBrightnessSensorController, atLeastOnce())
+                .setLightSensorEnabled(false);
+    }
+
+    @Test
+    public void testSetScreenOffBrightnessSensorDisabled_DisplayIsDisabled() {
+        doAnswer((Answer<Integer>) invocationOnMock ->
+                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC)
+                .when(() -> Settings.System.getIntForUser(any(ContentResolver.class),
+                        eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
+                        eq(UserHandle.USER_CURRENT)));
+        mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ false);
+
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1); // Run updatePowerState
+
+        verify(mHolder.screenOffBrightnessSensorController, atLeastOnce())
+                .setLightSensorEnabled(false);
+    }
+
+    @Test
+    public void testSetScreenOffBrightnessSensorDisabled_DisplayIsOn() {
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
+
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1); // Run updatePowerState
+
+        verify(mHolder.screenOffBrightnessSensorController, atLeastOnce())
+                .setLightSensorEnabled(false);
+    }
+
+    @Test
+    public void testSetScreenOffBrightnessSensorDisabled_DisplayIsAFollower() {
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        dpr.policy = DisplayPowerRequest.POLICY_OFF;
+
+        mHolder.dpc.onDisplayChanged(mHolder.hbmMetadata, /* leadDisplayId= */ 42);
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1); // Run updatePowerState
+
+        verify(mHolder.screenOffBrightnessSensorController, atLeastOnce())
+                .setLightSensorEnabled(false);
+    }
+
     private DisplayPowerControllerHolder createDisplayPowerController(int displayId,
             String uniqueId) {
+        return createDisplayPowerController(displayId, uniqueId, /* isEnabled= */ true);
+    }
+
+    private DisplayPowerControllerHolder createDisplayPowerController(int displayId,
+            String uniqueId, boolean isEnabled) {
         final DisplayPowerState displayPowerState = mock(DisplayPowerState.class);
         final DualRampAnimator<DisplayPowerState> animator = mock(DualRampAnimator.class);
         final AutomaticBrightnessController automaticBrightnessController =
@@ -470,10 +641,12 @@
         final BrightnessMappingStrategy brightnessMappingStrategy =
                 mock(BrightnessMappingStrategy.class);
         final HysteresisLevels hysteresisLevels = mock(HysteresisLevels.class);
+        final ScreenOffBrightnessSensorController screenOffBrightnessSensorController =
+                mock(ScreenOffBrightnessSensorController.class);
 
         TestInjector injector = new TestInjector(displayPowerState, animator,
                 automaticBrightnessController, wakelockController, brightnessMappingStrategy,
-                hysteresisLevels);
+                hysteresisLevels, screenOffBrightnessSensorController);
 
         final LogicalDisplay display = mock(LogicalDisplay.class);
         final DisplayDevice device = mock(DisplayDevice.class);
@@ -481,16 +654,17 @@
         final BrightnessSetting brightnessSetting = mock(BrightnessSetting.class);
         final DisplayDeviceConfig config = mock(DisplayDeviceConfig.class);
 
-        setUpDisplay(displayId, uniqueId, display, device, config);
+        setUpDisplay(displayId, uniqueId, display, device, config, isEnabled);
 
         final DisplayPowerController2 dpc = new DisplayPowerController2(
                 mContextSpy, injector, mDisplayPowerCallbacksMock, mHandler,
                 mSensorManagerMock, mDisplayBlankerMock, display,
                 mBrightnessTrackerMock, brightnessSetting, () -> {},
-                hbmMetadata);
+                hbmMetadata, false);
 
         return new DisplayPowerControllerHolder(dpc, displayPowerState, brightnessSetting, animator,
-                automaticBrightnessController, wakelockController);
+                automaticBrightnessController, wakelockController,
+                screenOffBrightnessSensorController, hbmMetadata);
     }
 
     /**
@@ -504,18 +678,24 @@
         public final DualRampAnimator<DisplayPowerState> animator;
         public final AutomaticBrightnessController automaticBrightnessController;
         public final WakelockController wakelockController;
+        public final ScreenOffBrightnessSensorController screenOffBrightnessSensorController;
+        public final HighBrightnessModeMetadata hbmMetadata;
 
         DisplayPowerControllerHolder(DisplayPowerController2 dpc,
                 DisplayPowerState displayPowerState, BrightnessSetting brightnessSetting,
                 DualRampAnimator<DisplayPowerState> animator,
                 AutomaticBrightnessController automaticBrightnessController,
-                WakelockController wakelockController) {
+                WakelockController wakelockController,
+                ScreenOffBrightnessSensorController screenOffBrightnessSensorController,
+                HighBrightnessModeMetadata hbmMetadata) {
             this.dpc = dpc;
             this.displayPowerState = displayPowerState;
             this.brightnessSetting = brightnessSetting;
             this.animator = animator;
             this.automaticBrightnessController = automaticBrightnessController;
             this.wakelockController = wakelockController;
+            this.screenOffBrightnessSensorController = screenOffBrightnessSensorController;
+            this.hbmMetadata = hbmMetadata;
         }
     }
 
@@ -526,18 +706,21 @@
         private final WakelockController mWakelockController;
         private final BrightnessMappingStrategy mBrightnessMappingStrategy;
         private final HysteresisLevels mHysteresisLevels;
+        private final ScreenOffBrightnessSensorController mScreenOffBrightnessSensorController;
 
         TestInjector(DisplayPowerState dps, DualRampAnimator<DisplayPowerState> animator,
                 AutomaticBrightnessController automaticBrightnessController,
                 WakelockController wakelockController,
                 BrightnessMappingStrategy brightnessMappingStrategy,
-                HysteresisLevels hysteresisLevels) {
+                HysteresisLevels hysteresisLevels,
+                ScreenOffBrightnessSensorController screenOffBrightnessSensorController) {
             mDisplayPowerState = dps;
             mAnimator = animator;
             mAutomaticBrightnessController = automaticBrightnessController;
             mWakelockController = wakelockController;
             mBrightnessMappingStrategy = brightnessMappingStrategy;
             mHysteresisLevels = hysteresisLevels;
+            mScreenOffBrightnessSensorController = screenOffBrightnessSensorController;
         }
 
         @Override
@@ -617,5 +800,13 @@
                 float minBrighteningThreshold, boolean potentialOldBrightnessRange) {
             return mHysteresisLevels;
         }
+
+        @Override
+        ScreenOffBrightnessSensorController getScreenOffBrightnessSensorController(
+                SensorManager sensorManager, Sensor lightSensor, Handler handler,
+                ScreenOffBrightnessSensorController.Clock clock, int[] sensorValueToLux,
+                BrightnessMappingStrategy brightnessMapper) {
+            return mScreenOffBrightnessSensorController;
+        }
     }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
index a8c3e4e..ebeef2e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -26,12 +26,15 @@
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.isA;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.when;
 
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.res.Resources;
@@ -44,7 +47,9 @@
 import android.os.Looper;
 import android.os.PowerManager;
 import android.os.SystemProperties;
+import android.os.UserHandle;
 import android.os.test.TestLooper;
+import android.provider.Settings;
 import android.util.FloatProperty;
 import android.view.Display;
 import android.view.DisplayInfo;
@@ -57,6 +62,7 @@
 import com.android.server.LocalServices;
 import com.android.server.am.BatteryStatsService;
 import com.android.server.display.RampAnimator.DualRampAnimator;
+import com.android.server.display.brightness.BrightnessEvent;
 import com.android.server.display.color.ColorDisplayService;
 import com.android.server.display.whitebalance.DisplayWhiteBalanceController;
 import com.android.server.policy.WindowManagerPolicy;
@@ -122,6 +128,7 @@
                 .spyStatic(SystemProperties.class)
                 .spyStatic(LocalServices.class)
                 .spyStatic(BatteryStatsService.class)
+                .spyStatic(Settings.System.class)
                 .startMocking();
         mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
         mClock = new OffsettableClock.Stopped();
@@ -140,8 +147,7 @@
                 ColorDisplayService.ColorDisplayServiceInternal.class));
         doAnswer((Answer<Void>) invocationOnMock -> null).when(BatteryStatsService::getService);
 
-        mProxSensor = setUpProxSensor();
-
+        setUpSensors();
         mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
     }
 
@@ -217,12 +223,13 @@
         mTestLooper.dispatchAll();
     }
 
-    private Sensor setUpProxSensor() throws Exception {
-        Sensor proxSensor = TestUtils.createSensor(
+    private void setUpSensors() throws Exception {
+        mProxSensor = TestUtils.createSensor(
                 Sensor.TYPE_PROXIMITY, Sensor.STRING_TYPE_PROXIMITY);
+        Sensor screenOffBrightnessSensor = TestUtils.createSensor(
+                Sensor.TYPE_LIGHT, Sensor.STRING_TYPE_LIGHT);
         when(mSensorManagerMock.getSensorList(eq(Sensor.TYPE_ALL)))
-                .thenReturn(List.of(proxSensor));
-        return proxSensor;
+                .thenReturn(List.of(mProxSensor, screenOffBrightnessSensor));
     }
 
     private SensorEventListener getSensorEventListener(Sensor sensor) {
@@ -232,14 +239,15 @@
     }
 
     private void setUpDisplay(int displayId, String uniqueId, LogicalDisplay logicalDisplayMock,
-            DisplayDevice displayDeviceMock, DisplayDeviceConfig displayDeviceConfigMock) {
+            DisplayDevice displayDeviceMock, DisplayDeviceConfig displayDeviceConfigMock,
+            boolean isEnabled) {
         DisplayInfo info = new DisplayInfo();
         DisplayDeviceInfo deviceInfo = new DisplayDeviceInfo();
 
         when(logicalDisplayMock.getDisplayIdLocked()).thenReturn(displayId);
         when(logicalDisplayMock.getPrimaryDisplayDeviceLocked()).thenReturn(displayDeviceMock);
         when(logicalDisplayMock.getDisplayInfoLocked()).thenReturn(info);
-        when(logicalDisplayMock.isEnabledLocked()).thenReturn(true);
+        when(logicalDisplayMock.isEnabledLocked()).thenReturn(isEnabled);
         when(logicalDisplayMock.isInTransitionLocked()).thenReturn(false);
         when(displayDeviceMock.getDisplayDeviceInfoLocked()).thenReturn(deviceInfo);
         when(displayDeviceMock.getUniqueId()).thenReturn(uniqueId);
@@ -256,7 +264,14 @@
         when(displayDeviceConfigMock.getAmbientLightSensor()).thenReturn(
                 new DisplayDeviceConfig.SensorData());
         when(displayDeviceConfigMock.getScreenOffBrightnessSensor()).thenReturn(
-                new DisplayDeviceConfig.SensorData());
+                new DisplayDeviceConfig.SensorData() {
+                    {
+                        type = Sensor.STRING_TYPE_LIGHT;
+                        name = null;
+                    }
+                });
+        when(displayDeviceConfigMock.getScreenOffBrightnessSensorValueToLux())
+                .thenReturn(new int[0]);
     }
 
     @Test
@@ -464,8 +479,164 @@
         verify(secondFollowerHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat());
     }
 
+    @Test
+    public void testDoesNotSetScreenStateForNonDefaultDisplayUntilBootCompleted() {
+        // We should still set screen state for the default display
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1);
+        verify(mHolder.displayPowerState, times(2)).setScreenState(anyInt());
+
+        mHolder = createDisplayPowerController(42, UNIQUE_ID);
+
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1);
+        verify(mHolder.displayPowerState, never()).setScreenState(anyInt());
+
+        mHolder.dpc.onBootCompleted();
+        advanceTime(1);
+        verify(mHolder.displayPowerState).setScreenState(anyInt());
+    }
+
+    @Test
+    public void testSetScreenOffBrightnessSensorEnabled_DisplayIsOff() {
+        doAnswer((Answer<Integer>) invocationOnMock ->
+                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC)
+                .when(() -> Settings.System.getIntForUser(any(ContentResolver.class),
+                        eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
+                        eq(UserHandle.USER_CURRENT)));
+
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        dpr.policy = DisplayPowerRequest.POLICY_OFF;
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1); // Run updatePowerState
+
+        verify(mHolder.screenOffBrightnessSensorController, atLeastOnce())
+                .setLightSensorEnabled(true);
+
+        // The display turns on and we use the brightness value recommended by
+        // ScreenOffBrightnessSensorController
+        clearInvocations(mHolder.screenOffBrightnessSensorController);
+        float brightness = 0.14f;
+        when(mHolder.screenOffBrightnessSensorController.getAutomaticScreenBrightness())
+                .thenReturn(brightness);
+        dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
+        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
+        when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness(
+                any(BrightnessEvent.class))).thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1); // Run updatePowerState
+
+        verify(mHolder.screenOffBrightnessSensorController, atLeastOnce())
+                .getAutomaticScreenBrightness();
+        verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat());
+    }
+
+    @Test
+    public void testSetScreenOffBrightnessSensorEnabled_DisplayIsInDoze() {
+        doAnswer((Answer<Integer>) invocationOnMock ->
+                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC)
+                .when(() -> Settings.System.getIntForUser(any(ContentResolver.class),
+                        eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
+                        eq(UserHandle.USER_CURRENT)));
+
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        dpr.policy = DisplayPowerRequest.POLICY_DOZE;
+        when(mResourcesMock.getBoolean(
+                com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing))
+                .thenReturn(true);
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1); // Run updatePowerState
+
+        verify(mHolder.screenOffBrightnessSensorController, atLeastOnce())
+                .setLightSensorEnabled(true);
+
+        // The display turns on and we use the brightness value recommended by
+        // ScreenOffBrightnessSensorController
+        clearInvocations(mHolder.screenOffBrightnessSensorController);
+        float brightness = 0.14f;
+        when(mHolder.screenOffBrightnessSensorController.getAutomaticScreenBrightness())
+                .thenReturn(brightness);
+        dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
+        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
+        when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness(
+                any(BrightnessEvent.class))).thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1); // Run updatePowerState
+
+        verify(mHolder.screenOffBrightnessSensorController, atLeastOnce())
+                .getAutomaticScreenBrightness();
+        verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat());
+    }
+
+    @Test
+    public void testSetScreenOffBrightnessSensorDisabled_AutoBrightnessIsDisabled() {
+        doAnswer((Answer<Integer>) invocationOnMock ->
+                Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL)
+                .when(() -> Settings.System.getIntForUser(any(ContentResolver.class),
+                        eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
+                        eq(UserHandle.USER_CURRENT)));
+
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        dpr.policy = DisplayPowerRequest.POLICY_OFF;
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1); // Run updatePowerState
+
+        verify(mHolder.screenOffBrightnessSensorController, atLeastOnce())
+                .setLightSensorEnabled(false);
+    }
+
+    @Test
+    public void testSetScreenOffBrightnessSensorDisabled_DisplayIsDisabled() {
+        doAnswer((Answer<Integer>) invocationOnMock ->
+                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC)
+                .when(() -> Settings.System.getIntForUser(any(ContentResolver.class),
+                        eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
+                        eq(UserHandle.USER_CURRENT)));
+        mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ false);
+
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1); // Run updatePowerState
+
+        verify(mHolder.screenOffBrightnessSensorController, atLeastOnce())
+                .setLightSensorEnabled(false);
+    }
+
+    @Test
+    public void testSetScreenOffBrightnessSensorDisabled_DisplayIsOn() {
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
+
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1); // Run updatePowerState
+
+        verify(mHolder.screenOffBrightnessSensorController, atLeastOnce())
+                .setLightSensorEnabled(false);
+    }
+
+    @Test
+    public void testSetScreenOffBrightnessSensorDisabled_DisplayIsAFollower() {
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        dpr.policy = DisplayPowerRequest.POLICY_OFF;
+
+        mHolder.dpc.onDisplayChanged(mHolder.hbmMetadata, /* leadDisplayId= */ 42);
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1); // Run updatePowerState
+
+        verify(mHolder.screenOffBrightnessSensorController, atLeastOnce())
+                .setLightSensorEnabled(false);
+    }
+
     private DisplayPowerControllerHolder createDisplayPowerController(int displayId,
             String uniqueId) {
+        return createDisplayPowerController(displayId, uniqueId, /* isEnabled= */ true);
+    }
+
+    private DisplayPowerControllerHolder createDisplayPowerController(int displayId,
+            String uniqueId, boolean isEnabled) {
         final DisplayPowerState displayPowerState = mock(DisplayPowerState.class);
         final DualRampAnimator<DisplayPowerState> animator = mock(DualRampAnimator.class);
         final AutomaticBrightnessController automaticBrightnessController =
@@ -473,9 +644,12 @@
         final BrightnessMappingStrategy brightnessMappingStrategy =
                 mock(BrightnessMappingStrategy.class);
         final HysteresisLevels hysteresisLevels = mock(HysteresisLevels.class);
+        final ScreenOffBrightnessSensorController screenOffBrightnessSensorController =
+                mock(ScreenOffBrightnessSensorController.class);
 
         DisplayPowerController.Injector injector = new TestInjector(displayPowerState, animator,
-                automaticBrightnessController, brightnessMappingStrategy, hysteresisLevels);
+                automaticBrightnessController, brightnessMappingStrategy, hysteresisLevels,
+                screenOffBrightnessSensorController);
 
         final LogicalDisplay display = mock(LogicalDisplay.class);
         final DisplayDevice device = mock(DisplayDevice.class);
@@ -483,16 +657,16 @@
         final BrightnessSetting brightnessSetting = mock(BrightnessSetting.class);
         final DisplayDeviceConfig config = mock(DisplayDeviceConfig.class);
 
-        setUpDisplay(displayId, uniqueId, display, device, config);
+        setUpDisplay(displayId, uniqueId, display, device, config, isEnabled);
 
         final DisplayPowerController dpc = new DisplayPowerController(
                 mContextSpy, injector, mDisplayPowerCallbacksMock, mHandler,
                 mSensorManagerMock, mDisplayBlankerMock, display,
                 mBrightnessTrackerMock, brightnessSetting, () -> {},
-                hbmMetadata);
+                hbmMetadata, false);
 
         return new DisplayPowerControllerHolder(dpc, displayPowerState, brightnessSetting, animator,
-                automaticBrightnessController);
+                automaticBrightnessController, screenOffBrightnessSensorController, hbmMetadata);
     }
 
     /**
@@ -505,16 +679,22 @@
         public final BrightnessSetting brightnessSetting;
         public final DualRampAnimator<DisplayPowerState> animator;
         public final AutomaticBrightnessController automaticBrightnessController;
+        public final ScreenOffBrightnessSensorController screenOffBrightnessSensorController;
+        public final HighBrightnessModeMetadata hbmMetadata;
 
         DisplayPowerControllerHolder(DisplayPowerController dpc,
                 DisplayPowerState displayPowerState, BrightnessSetting brightnessSetting,
                 DualRampAnimator<DisplayPowerState> animator,
-                AutomaticBrightnessController automaticBrightnessController) {
+                AutomaticBrightnessController automaticBrightnessController,
+                ScreenOffBrightnessSensorController screenOffBrightnessSensorController,
+                HighBrightnessModeMetadata hbmMetadata) {
             this.dpc = dpc;
             this.displayPowerState = displayPowerState;
             this.brightnessSetting = brightnessSetting;
             this.animator = animator;
             this.automaticBrightnessController = automaticBrightnessController;
+            this.screenOffBrightnessSensorController = screenOffBrightnessSensorController;
+            this.hbmMetadata = hbmMetadata;
         }
     }
 
@@ -524,16 +704,19 @@
         private final AutomaticBrightnessController mAutomaticBrightnessController;
         private final BrightnessMappingStrategy mBrightnessMappingStrategy;
         private final HysteresisLevels mHysteresisLevels;
+        private final ScreenOffBrightnessSensorController mScreenOffBrightnessSensorController;
 
         TestInjector(DisplayPowerState dps, DualRampAnimator<DisplayPowerState> animator,
                 AutomaticBrightnessController automaticBrightnessController,
                 BrightnessMappingStrategy brightnessMappingStrategy,
-                HysteresisLevels hysteresisLevels) {
+                HysteresisLevels hysteresisLevels,
+                ScreenOffBrightnessSensorController screenOffBrightnessSensorController) {
             mDisplayPowerState = dps;
             mAnimator = animator;
             mAutomaticBrightnessController = automaticBrightnessController;
             mBrightnessMappingStrategy = brightnessMappingStrategy;
             mHysteresisLevels = hysteresisLevels;
+            mScreenOffBrightnessSensorController = screenOffBrightnessSensorController;
         }
 
         @Override
@@ -597,5 +780,13 @@
                 float minBrighteningThreshold, boolean potentialOldBrightnessRange) {
             return mHysteresisLevels;
         }
+
+        @Override
+        ScreenOffBrightnessSensorController getScreenOffBrightnessSensorController(
+                SensorManager sensorManager, Sensor lightSensor, Handler handler,
+                ScreenOffBrightnessSensorController.Clock clock, int[] sensorValueToLux,
+                BrightnessMappingStrategy brightnessMapper) {
+            return mScreenOffBrightnessSensorController;
+        }
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index 89a5b12..8994a48 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -133,6 +133,7 @@
     private static final int TEST_USER_ID1 = 101;
     private static final int TEST_USER_ID2 = 102;
     private static final int TEST_USER_ID3 = 103;
+    private static final int SYSTEM_USER_ID = UserHandle.SYSTEM.getIdentifier();
     private static final int NONEXIST_USER_ID = 2;
     private static final int TEST_PRE_CREATED_USER_ID = 103;
 
@@ -231,6 +232,31 @@
     }
 
     @Test
+    public void testStartUser_sendsNoBroadcastsForSystemUserInNonHeadlessMode() {
+        setUpUser(SYSTEM_USER_ID, UserInfo.FLAG_SYSTEM, /* preCreated= */ false,
+                UserManager.USER_TYPE_FULL_SYSTEM);
+        mockIsHeadlessSystemUserMode(false);
+
+        mUserController.startUser(SYSTEM_USER_ID, USER_START_MODE_FOREGROUND);
+
+        assertWithMessage("Broadcasts for starting the system user in non-headless mode")
+                .that(mInjector.mSentIntents).isEmpty();
+    }
+
+    @Test
+    public void testStartUser_sendsBroadcastsForSystemUserInHeadlessMode() {
+        setUpUser(SYSTEM_USER_ID, UserInfo.FLAG_SYSTEM, /* preCreated= */ false,
+                UserManager.USER_TYPE_SYSTEM_HEADLESS);
+        mockIsHeadlessSystemUserMode(true);
+
+        mUserController.startUser(SYSTEM_USER_ID, USER_START_MODE_FOREGROUND);
+
+        assertWithMessage("Broadcasts for starting the system user in headless mode")
+                .that(getActions(mInjector.mSentIntents)).containsExactly(
+                        Intent.ACTION_USER_STARTED, Intent.ACTION_USER_STARTING);
+    }
+
+    @Test
     public void testStartUser_displayAssignmentFailed() {
         doReturn(UserManagerInternal.USER_ASSIGNMENT_RESULT_FAILURE)
                 .when(mInjector.mUserManagerInternalMock)
@@ -999,6 +1025,10 @@
         }
     }
 
+    private void mockIsHeadlessSystemUserMode(boolean value) {
+        when(mInjector.isHeadlessSystemUserMode()).thenReturn(value);
+    }
+
     private void mockIsUsersOnSecondaryDisplaysEnabled(boolean value) {
         when(mInjector.isUsersOnSecondaryDisplaysEnabled()).thenReturn(value);
     }
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/InputManagerMockHelper.java b/services/tests/servicestests/src/com/android/server/companion/virtual/InputManagerMockHelper.java
index 51bd5b0f..3cc6b01 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/InputManagerMockHelper.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/InputManagerMockHelper.java
@@ -79,7 +79,7 @@
         InputManager.resetInstance(mIInputManagerMock);
     }
 
-    private Void handleNativeOpenInputDevice(InvocationOnMock inv) {
+    private long handleNativeOpenInputDevice(InvocationOnMock inv) {
         Objects.requireNonNull(mDevicesChangedListener,
                 "InputController did not register an InputDevicesChangedListener.");
 
@@ -101,6 +101,7 @@
         }
         // Process the device added notification.
         mTestableLooper.processAllMessages();
-        return null;
+        // Return a placeholder pointer to the native input device.
+        return 1L;
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/NetworkTimeUpdateServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/NetworkTimeUpdateServiceTest.java
index 1001422..2f431bd 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/NetworkTimeUpdateServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/NetworkTimeUpdateServiceTest.java
@@ -22,6 +22,7 @@
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.app.time.UnixEpochTime;
@@ -60,7 +61,28 @@
     }
 
     @Test
-    public void engineImpl_refreshIfRequiredAndReschedule_success() {
+    public void engineImpl_refreshAndRescheduleIfRequired_nullNetwork() {
+        mFakeElapsedRealtimeClock.setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS);
+
+        int normalPollingIntervalMillis = 7777777;
+        int shortPollingIntervalMillis = 3333;
+        int tryAgainTimesMax = 5;
+        NetworkTimeUpdateService.Engine engine = new NetworkTimeUpdateService.EngineImpl(
+                mFakeElapsedRealtimeClock,
+                normalPollingIntervalMillis, shortPollingIntervalMillis, tryAgainTimesMax,
+                mMockNtpTrustedTime);
+
+        RefreshCallbacks mockCallback = mock(RefreshCallbacks.class);
+        // Trigger the engine's logic.
+        engine.refreshAndRescheduleIfRequired(null, "Test", mockCallback);
+
+        // Expect nothing to have happened.
+        verifyNoMoreInteractions(mMockNtpTrustedTime);
+        verifyNoMoreInteractions(mockCallback);
+    }
+
+    @Test
+    public void engineImpl_refreshAndRescheduleIfRequired_success() {
         mFakeElapsedRealtimeClock.setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS);
 
         int normalPollingIntervalMillis = 7777777;
@@ -83,7 +105,7 @@
 
         RefreshCallbacks mockCallback = mock(RefreshCallbacks.class);
         // Trigger the engine's logic.
-        engine.refreshIfRequiredAndReschedule(mDummyNetwork, "Test", mockCallback);
+        engine.refreshAndRescheduleIfRequired(mDummyNetwork, "Test", mockCallback);
 
         // Expect the refresh attempt to have been made.
         verify(mMockNtpTrustedTime).forceRefresh(mDummyNetwork);
@@ -98,7 +120,7 @@
     }
 
     @Test
-    public void engineImpl_refreshIfRequiredAndReschedule_failThenFailRepeatedly() {
+    public void engineImpl_refreshAndRescheduleIfRequired_failThenFailRepeatedly() {
         mFakeElapsedRealtimeClock.setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS);
 
         int normalPollingIntervalMillis = 7777777;
@@ -120,7 +142,7 @@
             RefreshCallbacks mockCallback = mock(RefreshCallbacks.class);
 
             // Trigger the engine's logic.
-            engine.refreshIfRequiredAndReschedule(mDummyNetwork, "Test", mockCallback);
+            engine.refreshAndRescheduleIfRequired(mDummyNetwork, "Test", mockCallback);
 
             // Expect a refresh attempt each time: there's no currently cached result.
             verify(mMockNtpTrustedTime).forceRefresh(mDummyNetwork);
@@ -141,7 +163,7 @@
     }
 
     @Test
-    public void engineImpl_refreshIfRequiredAndReschedule_successThenFailRepeatedly() {
+    public void engineImpl_refreshAndRescheduleIfRequired_successThenFailRepeatedly() {
         mFakeElapsedRealtimeClock.setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS);
 
         int normalPollingIntervalMillis = 7777777;
@@ -168,7 +190,7 @@
             RefreshCallbacks mockCallback = mock(RefreshCallbacks.class);
 
             // Trigger the engine's logic.
-            engine.refreshIfRequiredAndReschedule(mDummyNetwork, "Test", mockCallback);
+            engine.refreshAndRescheduleIfRequired(mDummyNetwork, "Test", mockCallback);
 
             // Expect the refresh attempt to have been made: there is no cached network time
             // initially.
@@ -182,7 +204,7 @@
         }
 
         // Increment the current time by enough so that an attempt to refresh the time should be
-        // made every time refreshIfRequiredAndReschedule() is called.
+        // made every time refreshAndRescheduleIfRequired() is called.
         mFakeElapsedRealtimeClock.incrementMillis(normalPollingIntervalMillis);
 
         // Test multiple follow-up calls.
@@ -195,7 +217,7 @@
             RefreshCallbacks mockCallback = mock(RefreshCallbacks.class);
 
             // Trigger the engine's logic.
-            engine.refreshIfRequiredAndReschedule(mDummyNetwork, "Test", mockCallback);
+            engine.refreshAndRescheduleIfRequired(mDummyNetwork, "Test", mockCallback);
 
             // Expect a refresh attempt each time as the cached network time is too old.
             verify(mMockNtpTrustedTime).forceRefresh(mDummyNetwork);
@@ -221,7 +243,7 @@
     }
 
     @Test
-    public void engineImpl_refreshIfRequiredAndReschedule_successThenFail_tryAgainTimesZero() {
+    public void engineImpl_refreshAndRescheduleIfRequired_successThenFail_tryAgainTimesZero() {
         mFakeElapsedRealtimeClock.setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS);
 
         int normalPollingIntervalMillis = 7777777;
@@ -248,7 +270,7 @@
             RefreshCallbacks mockCallback = mock(RefreshCallbacks.class);
 
             // Trigger the engine's logic.
-            engine.refreshIfRequiredAndReschedule(mDummyNetwork, "Test", mockCallback);
+            engine.refreshAndRescheduleIfRequired(mDummyNetwork, "Test", mockCallback);
 
             // Expect the refresh attempt to have been made: there is no cached network time
             // initially.
@@ -262,7 +284,7 @@
         }
 
         // Increment the current time by enough so that an attempt to refresh the time should be
-        // made every time refreshIfRequiredAndReschedule() is called.
+        // made every time refreshAndRescheduleIfRequired() is called.
         mFakeElapsedRealtimeClock.incrementMillis(normalPollingIntervalMillis);
 
         // Test multiple follow-up calls.
@@ -275,7 +297,7 @@
             RefreshCallbacks mockCallback = mock(RefreshCallbacks.class);
 
             // Trigger the engine's logic.
-            engine.refreshIfRequiredAndReschedule(mDummyNetwork, "Test", mockCallback);
+            engine.refreshAndRescheduleIfRequired(mDummyNetwork, "Test", mockCallback);
 
             // Expect a refresh attempt each time as the cached network time is too old.
             verify(mMockNtpTrustedTime).forceRefresh(mDummyNetwork);
@@ -297,7 +319,7 @@
     }
 
     @Test
-    public void engineImpl_refreshIfRequiredAndReschedule_successThenFail_tryAgainTimesNegative() {
+    public void engineImpl_refreshAndRescheduleIfRequired_successThenFail_tryAgainTimesNegative() {
         mFakeElapsedRealtimeClock.setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS);
 
         int normalPollingIntervalMillis = 7777777;
@@ -324,7 +346,7 @@
             RefreshCallbacks mockCallback = mock(RefreshCallbacks.class);
 
             // Trigger the engine's logic.
-            engine.refreshIfRequiredAndReschedule(mDummyNetwork, "Test", mockCallback);
+            engine.refreshAndRescheduleIfRequired(mDummyNetwork, "Test", mockCallback);
 
             // Expect the refresh attempt to have been made: there is no cached network time
             // initially.
@@ -338,7 +360,7 @@
         }
 
         // Increment the current time by enough so that an attempt to refresh the time should be
-        // made every time refreshIfRequiredAndReschedule() is called.
+        // made every time refreshAndRescheduleIfRequired() is called.
         mFakeElapsedRealtimeClock.incrementMillis(normalPollingIntervalMillis);
 
         // Test multiple follow-up calls.
@@ -351,7 +373,7 @@
             RefreshCallbacks mockCallback = mock(RefreshCallbacks.class);
 
             // Trigger the engine's logic.
-            engine.refreshIfRequiredAndReschedule(mDummyNetwork, "Test", mockCallback);
+            engine.refreshAndRescheduleIfRequired(mDummyNetwork, "Test", mockCallback);
 
             // Expect a refresh attempt each time as the cached network time is too old.
             verify(mMockNtpTrustedTime).forceRefresh(mDummyNetwork);
@@ -373,7 +395,7 @@
     }
 
     @Test
-    public void engineImpl_refreshIfRequiredAndReschedule_successFailSuccess() {
+    public void engineImpl_refreshAndRescheduleIfRequired_successFailSuccess() {
         mFakeElapsedRealtimeClock.setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS);
 
         int normalPollingIntervalMillis = 7777777;
@@ -398,7 +420,7 @@
             RefreshCallbacks mockCallback = mock(RefreshCallbacks.class);
 
             // Trigger the engine's logic.
-            engine.refreshIfRequiredAndReschedule(mDummyNetwork, "Test", mockCallback);
+            engine.refreshAndRescheduleIfRequired(mDummyNetwork, "Test", mockCallback);
 
             // Expect the refresh attempt to have been made: there is no cached network time
             // initially.
@@ -413,7 +435,7 @@
         }
 
         // Increment the current time by enough so that the cached time result is too old and an
-        // attempt to refresh the time should be made every time refreshIfRequiredAndReschedule() is
+        // attempt to refresh the time should be made every time refreshAndRescheduleIfRequired() is
         // called.
         mFakeElapsedRealtimeClock.incrementMillis(normalPollingIntervalMillis);
 
@@ -426,7 +448,7 @@
             RefreshCallbacks mockCallback = mock(RefreshCallbacks.class);
 
             // Trigger the engine's logic.
-            engine.refreshIfRequiredAndReschedule(mDummyNetwork, "Test", mockCallback);
+            engine.refreshAndRescheduleIfRequired(mDummyNetwork, "Test", mockCallback);
 
             // Expect the refresh attempt to have been made: the timeResult is too old.
             verify(mMockNtpTrustedTime).forceRefresh(mDummyNetwork);
@@ -458,7 +480,7 @@
             RefreshCallbacks mockCallback = mock(RefreshCallbacks.class);
 
             // Trigger the engine's logic.
-            engine.refreshIfRequiredAndReschedule(mDummyNetwork, "Test", mockCallback);
+            engine.refreshAndRescheduleIfRequired(mDummyNetwork, "Test", mockCallback);
 
             // Expect the refresh attempt to have been made: the timeResult is too old.
             verify(mMockNtpTrustedTime).forceRefresh(mDummyNetwork);
@@ -473,12 +495,12 @@
     }
 
     /**
-     * Confirms that if a refreshIfRequiredAndReschedule() call is made, e.g. for reasons besides
+     * Confirms that if a refreshAndRescheduleIfRequired() call is made, e.g. for reasons besides
      * scheduled alerts, and the latest time is not too old, then an NTP refresh won't be attempted.
      * A suggestion will still be made.
      */
     @Test
-    public void engineImpl_refreshIfRequiredAndReschedule_noRefreshIfLatestIsFresh() {
+    public void engineImpl_refreshAndRescheduleIfRequired_noRefreshIfLatestIsFresh() {
         mFakeElapsedRealtimeClock.setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS);
 
         int normalPollingIntervalMillis = 7777777;
@@ -498,7 +520,7 @@
 
         RefreshCallbacks mockCallback = mock(RefreshCallbacks.class);
         // Trigger the engine's logic.
-        engine.refreshIfRequiredAndReschedule(mDummyNetwork, "Test", mockCallback);
+        engine.refreshAndRescheduleIfRequired(mDummyNetwork, "Test", mockCallback);
 
         // Expect no refresh attempt to have been made.
         verify(mMockNtpTrustedTime, never()).forceRefresh(any());
@@ -516,11 +538,11 @@
     }
 
     /**
-     * Confirms that if a refreshIfRequiredAndReschedule() call is made, e.g. for reasons besides
+     * Confirms that if a refreshAndRescheduleIfRequired() call is made, e.g. for reasons besides
      * scheduled alerts, and the latest time is too old, then an NTP refresh will be attempted.
      */
     @Test
-    public void engineImpl_refreshIfRequiredAndReschedule_failureHandlingAfterLatestIsTooOld() {
+    public void engineImpl_refreshAndRescheduleIfRequired_failureHandlingAfterLatestIsTooOld() {
         mFakeElapsedRealtimeClock.setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS);
 
         int normalPollingIntervalMillis = 7777777;
@@ -541,7 +563,7 @@
 
         RefreshCallbacks mockCallback = mock(RefreshCallbacks.class);
         // Trigger the engine's logic.
-        engine.refreshIfRequiredAndReschedule(mDummyNetwork, "Test", mockCallback);
+        engine.refreshAndRescheduleIfRequired(mDummyNetwork, "Test", mockCallback);
 
         // Expect a refresh attempt to have been made.
         verify(mMockNtpTrustedTime, times(1)).forceRefresh(mDummyNetwork);
@@ -556,11 +578,11 @@
     }
 
     /**
-     * Confirms that if a refreshIfRequiredAndReschedule() call is made and there was a recently
+     * Confirms that if a refreshAndRescheduleIfRequired() call is made and there was a recently
      * failed refresh, then another won't be scheduled too soon.
      */
     @Test
-    public void engineImpl_refreshIfRequiredAndReschedule_minimumRefreshTimeEnforced() {
+    public void engineImpl_refreshAndRescheduleIfRequired_minimumRefreshTimeEnforced() {
         mFakeElapsedRealtimeClock.setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS);
 
         int normalPollingIntervalMillis = 7777777;
@@ -574,7 +596,7 @@
         NtpTrustedTime.TimeResult timeResult = createNtpTimeResult(
                 mFakeElapsedRealtimeClock.getElapsedRealtimeMillis());
 
-        // Simulate an initial call to refreshIfRequiredAndReschedule() prime the "last refresh
+        // Simulate an initial call to refreshAndRescheduleIfRequired() prime the "last refresh
         // attempt" time. A cached time value is available, but it's too old but the refresh
         // attempt will fail.
         long lastRefreshAttemptElapsedMillis;
@@ -586,7 +608,7 @@
 
             RefreshCallbacks mockCallback = mock(RefreshCallbacks.class);
             // Trigger the engine's logic.
-            engine.refreshIfRequiredAndReschedule(mDummyNetwork, "Test", mockCallback);
+            engine.refreshAndRescheduleIfRequired(mDummyNetwork, "Test", mockCallback);
 
             // Expect a refresh attempt to have been made.
             verify(mMockNtpTrustedTime, times(1)).forceRefresh(mDummyNetwork);
@@ -606,7 +628,7 @@
             reset(mMockNtpTrustedTime);
         }
 
-        // Simulate a second call to refreshIfRequiredAndReschedule() very soon after the first, as
+        // Simulate a second call to refreshAndRescheduleIfRequired() very soon after the first, as
         // might happen if there were a network state change.
         // The cached time value is available, but it's still too old. Because the last call was so
         // recent, no refresh should take place and the next scheduled refresh time should be
@@ -619,7 +641,7 @@
 
             RefreshCallbacks mockCallback = mock(RefreshCallbacks.class);
             // Trigger the engine's logic.
-            engine.refreshIfRequiredAndReschedule(mDummyNetwork, "Test", mockCallback);
+            engine.refreshAndRescheduleIfRequired(mDummyNetwork, "Test", mockCallback);
 
             // Expect no refresh attempt to have been made: time elapsed isn't enough.
             verify(mMockNtpTrustedTime, never()).forceRefresh(any());
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 28e493e..e3cb5fb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -145,7 +145,8 @@
 public class ActivityStarterTests extends WindowTestsBase {
 
     private static final String ENABLE_DEFAULT_RESCIND_BAL_PRIVILEGES_FROM_PENDING_INTENT_SENDER =
-            "enable_default_rescind_bal_privileges_from_pending_intent_sender";
+            "DefaultRescindBalPrivilegesFromPendingIntentSender__"
+                    + "enable_default_rescind_bal_privileges_from_pending_intent_sender";
     private static final int PRECONDITION_NO_CALLER_APP = 1;
     private static final int PRECONDITION_NO_INTENT_COMPONENT = 1 << 1;
     private static final int PRECONDITION_NO_ACTIVITY_INFO = 1 << 2;
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index d4374a9..fd54293 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -1049,7 +1049,8 @@
 
         @Override
         public int startAssistantActivity(@NonNull IBinder token, @NonNull Intent intent,
-                @Nullable String resolvedType, @Nullable String attributionTag) {
+                @Nullable String resolvedType, @NonNull String attributionTag,
+                @NonNull Bundle bundle) {
             synchronized (this) {
                 if (mImpl == null) {
                     Slog.w(TAG, "startAssistantActivity without running voice interaction service");
@@ -1060,7 +1061,7 @@
                 final long caller = Binder.clearCallingIdentity();
                 try {
                     return mImpl.startAssistantActivityLocked(attributionTag, callingPid,
-                            callingUid, token, intent, resolvedType);
+                            callingUid, token, intent, resolvedType, bundle);
                 } finally {
                     Binder.restoreCallingIdentity(caller);
                 }
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index ad0e921..96b69f8 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -29,7 +29,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
-import android.app.ActivityOptions;
 import android.app.ActivityTaskManager;
 import android.app.AppGlobals;
 import android.app.ApplicationExitInfo;
@@ -376,7 +375,8 @@
 
     @GuardedBy("this")
     public int startAssistantActivityLocked(@Nullable String callingFeatureId, int callingPid,
-            int callingUid, IBinder token, Intent intent, String resolvedType) {
+            int callingUid, IBinder token, Intent intent, String resolvedType,
+            @NonNull Bundle bundle) {
         try {
             if (mActiveSession == null || token != mActiveSession.mToken) {
                 Slog.w(TAG, "startAssistantActivity does not match active session");
@@ -388,10 +388,10 @@
             }
             intent = new Intent(intent);
             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-            final ActivityOptions options = ActivityOptions.makeBasic();
-            options.setLaunchActivityType(ACTIVITY_TYPE_ASSISTANT);
+            // TODO: make the key public hidden
+            bundle.putInt("android.activity.activityType", ACTIVITY_TYPE_ASSISTANT);
             return mAtm.startAssistantActivity(mComponent.getPackageName(), callingFeatureId,
-                    callingPid, callingUid, intent, resolvedType, options.toBundle(), mUser);
+                    callingPid, callingUid, intent, resolvedType, bundle, mUser);
         } catch (RemoteException e) {
             throw new IllegalStateException("Unexpected remote error", e);
         }
diff --git a/tests/FlickerTests/AndroidTest.xml b/tests/FlickerTests/AndroidTest.xml
index 707b522..f2ffc19 100644
--- a/tests/FlickerTests/AndroidTest.xml
+++ b/tests/FlickerTests/AndroidTest.xml
@@ -24,7 +24,11 @@
     </target_preparer>
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
         <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1" />
+        <option name="run-command" value="settings put system show_touches 1" />
+        <option name="run-command" value="settings put system pointer_location 1" />
         <option name="teardown-command" value="settings delete secure show_ime_with_hard_keyboard" />
+        <option name="teardown-command" value="settings delete system show_touches" />
+        <option name="teardown-command" value="settings delete system pointer_location" />
     </target_preparer>
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true"/>
diff --git a/tools/aapt2/SdkConstants.cpp b/tools/aapt2/SdkConstants.cpp
index a7c5479..a766bd4 100644
--- a/tools/aapt2/SdkConstants.cpp
+++ b/tools/aapt2/SdkConstants.cpp
@@ -26,8 +26,8 @@
 namespace aapt {
 
 static ApiVersion sDevelopmentSdkLevel = 10000;
-static const auto sDevelopmentSdkCodeNames =
-    std::unordered_set<StringPiece>({"Q", "R", "S", "Sv2", "Tiramisu", "UpsideDownCake"});
+static const auto sDevelopmentSdkCodeNames = std::unordered_set<StringPiece>(
+    {"Q", "R", "S", "Sv2", "Tiramisu", "UpsideDownCake", "VanillaIceCream"});
 
 static const std::vector<std::pair<uint16_t, ApiVersion>> sAttrIdMap = {
     {0x021c, 1},
diff --git a/tools/processors/immutability/TEST_MAPPING b/tools/processors/immutability/TEST_MAPPING
deleted file mode 100644
index 4e8e238..0000000
--- a/tools/processors/immutability/TEST_MAPPING
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-    "presubmit": [
-        {
-            "name": "ImmutabilityAnnotationProcessorUnitTests"
-        }
-    ]
-}