Merge "Fix some formatting issues in setRequireUserAction" into udc-dev
diff --git a/OWNERS b/OWNERS
index 09a721f..7bea58b 100644
--- a/OWNERS
+++ b/OWNERS
@@ -35,3 +35,5 @@
 per-file framework-jarjar-rules.txt = file:platform/build/soong:/OWNERS #{LAST_RESORT_SUGGESTION}
 per-file TestProtoLibraries.bp = file:platform/platform_testing:/libraries/health/OWNERS
 per-file TestProtoLibraries.bp = file:platform/tools/tradefederation:/OWNERS
+
+per-file ZYGOTE_OWNERS = file:/ZYGOTE_OWNERS
diff --git a/ZYGOTE_OWNERS b/ZYGOTE_OWNERS
index 90a185b..f6d15e0 100644
--- a/ZYGOTE_OWNERS
+++ b/ZYGOTE_OWNERS
@@ -1,4 +1,3 @@
-calin@google.com
 chriswailes@google.com
 maco@google.com
 narayan@google.com
diff --git a/apct-tests/perftests/core/src/android/input/MotionPredictorBenchmark.kt b/apct-tests/perftests/core/src/android/input/MotionPredictorBenchmark.kt
index fabf889..9482591 100644
--- a/apct-tests/perftests/core/src/android/input/MotionPredictorBenchmark.kt
+++ b/apct-tests/perftests/core/src/android/input/MotionPredictorBenchmark.kt
@@ -33,7 +33,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 
 import org.junit.After
-import org.junit.Assert.assertEquals
 import org.junit.Assert.assertTrue
 import org.junit.Before
 import org.junit.Rule
@@ -132,8 +131,7 @@
             predictor.record(moveEvent)
             val predictionTime = eventTime + eventInterval
             val predicted = predictor.predict(predictionTime.toNanos())
-            assertEquals(1, predicted.size)
-            assertTrue(predicted[0].eventTime <= (predictionTime + offset).toMillis())
+            assertTrue(predicted.eventTime <= (predictionTime + offset).toMillis())
         }
     }
 
diff --git a/core/api/current.txt b/core/api/current.txt
index 2b1cb0e..53dcd75 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -4492,8 +4492,8 @@
     method public void openOptionsMenu();
     method public void overrideActivityTransition(int, @AnimRes int, @AnimRes int);
     method public void overrideActivityTransition(int, @AnimRes int, @AnimRes int, @ColorInt int);
-    method public void overridePendingTransition(int, int);
-    method public void overridePendingTransition(int, int, int);
+    method @Deprecated public void overridePendingTransition(int, int);
+    method @Deprecated public void overridePendingTransition(int, int, int);
     method public void postponeEnterTransition();
     method public void recreate();
     method public void registerActivityLifecycleCallbacks(@NonNull android.app.Application.ActivityLifecycleCallbacks);
@@ -19593,7 +19593,7 @@
   public final class VirtualDisplayConfig implements android.os.Parcelable {
     method public int describeContents();
     method public int getDensityDpi();
-    method @NonNull public java.util.List<java.lang.String> getDisplayCategories();
+    method @NonNull public java.util.Set<java.lang.String> getDisplayCategories();
     method public int getFlags();
     method public int getHeight();
     method @NonNull public String getName();
@@ -19608,7 +19608,7 @@
     ctor public VirtualDisplayConfig.Builder(@NonNull String, @IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int);
     method @NonNull public android.hardware.display.VirtualDisplayConfig.Builder addDisplayCategory(@NonNull String);
     method @NonNull public android.hardware.display.VirtualDisplayConfig build();
-    method @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setDisplayCategories(@NonNull java.util.List<java.lang.String>);
+    method @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setDisplayCategories(@NonNull java.util.Set<java.lang.String>);
     method @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setFlags(int);
     method @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setRequestedRefreshRate(@FloatRange(from=0.0f) float);
     method @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setSurface(@Nullable android.view.Surface);
@@ -51443,7 +51443,7 @@
   public final class MotionPredictor {
     ctor public MotionPredictor(@NonNull android.content.Context);
     method public boolean isPredictionAvailable(int, int);
-    method @NonNull public java.util.List<android.view.MotionEvent> predict(long);
+    method @Nullable public android.view.MotionEvent predict(long);
     method public void record(@NonNull android.view.MotionEvent);
   }
 
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index d9c7a27..125e727 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -6621,7 +6621,9 @@
      * the incoming activity.  Use 0 for no animation.
      * @param exitAnim A resource ID of the animation resource to use for
      * the outgoing activity.  Use 0 for no animation.
+     * @deprecated Use {@link #overrideActivityTransition(int, int, int)}} instead.
      */
+    @Deprecated
     public void overridePendingTransition(int enterAnim, int exitAnim) {
         overridePendingTransition(enterAnim, exitAnim, 0);
     }
@@ -6644,7 +6646,9 @@
      * the outgoing activity.  Use 0 for no animation.
      * @param backgroundColor The background color to use for the background during the animation if
      * the animation requires a background. Set to 0 to not override the default color.
+     * @deprecated Use {@link #overrideActivityTransition(int, int, int, int)}} instead.
      */
+    @Deprecated
     public void overridePendingTransition(int enterAnim, int exitAnim, int backgroundColor) {
         ActivityClient.getInstance().overridePendingTransition(mToken, getPackageName(), enterAnim,
                 exitAnim, backgroundColor);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index dfdfd0e..a3ada76 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -1154,6 +1154,11 @@
         }
 
         @Override
+        public final void schedulePing(RemoteCallback pong) {
+            sendMessage(H.PING, pong);
+        }
+
+        @Override
         public final void bindApplication(String processName, ApplicationInfo appInfo,
                 String sdkSandboxClientAppVolumeUuid, String sdkSandboxClientAppPackage,
                 ProviderInfoList providerList, ComponentName instrumentationName,
@@ -2154,6 +2159,7 @@
         public static final int DUMP_GFXINFO = 165;
         public static final int DUMP_RESOURCES = 166;
         public static final int TIMEOUT_SERVICE = 167;
+        public static final int PING = 168;
 
         public static final int INSTRUMENT_WITHOUT_RESTART = 170;
         public static final int FINISH_INSTRUMENTATION_WITHOUT_RESTART = 171;
@@ -2209,6 +2215,7 @@
                         return "FINISH_INSTRUMENTATION_WITHOUT_RESTART";
                     case DUMP_RESOURCES: return "DUMP_RESOURCES";
                     case TIMEOUT_SERVICE: return "TIMEOUT_SERVICE";
+                    case PING: return "PING";
                 }
             }
             return Integer.toString(code);
@@ -2292,6 +2299,9 @@
                     handleTimeoutService((IBinder) msg.obj, msg.arg1);
                     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                     break;
+                case PING:
+                    ((RemoteCallback) msg.obj).sendResult(null);
+                    break;
                 case CONFIGURATION_CHANGED:
                     mConfigurationController.handleConfigurationChanged((Configuration) msg.obj);
                     break;
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index dad9b43..4f77203 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -172,4 +172,5 @@
             in TranslationSpec targetSpec, in List<AutofillId> viewIds,
             in UiTranslationSpec uiTranslationSpec);
     void scheduleTimeoutService(IBinder token, int startId);
+    void schedulePing(in RemoteCallback pong);
 }
diff --git a/core/java/android/app/LocaleConfig.java b/core/java/android/app/LocaleConfig.java
index f4cd60d..97cc706 100644
--- a/core/java/android/app/LocaleConfig.java
+++ b/core/java/android/app/LocaleConfig.java
@@ -58,10 +58,10 @@
  *
  * @attr ref android.R.styleable#LocaleConfig_Locale_name
  * @attr ref android.R.styleable#AndroidManifestApplication_localeConfig
- *
- * <p>For more information about the LocaleConfig overridden by the application, see
- * TODO(b/261528306): add link to guide
  */
+// Add following to last Note: when guide is written:
+// For more information about the LocaleConfig overridden by the application, see TODO(b/261528306):
+// add link to guide
 public class LocaleConfig implements Parcelable {
     private static final String TAG = "LocaleConfig";
     public static final String TAG_LOCALE_CONFIG = "locale-config";
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index de4f619..a522cc0 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -801,6 +801,119 @@
     }
 
     /**
+     * Listener for any changes to {@link com.android.server.companion.transport.Transport}.
+     *
+     * @hide
+     */
+    public interface OnTransportsChangedListener {
+        /**
+         * Invoked when a change occurs to any of the transports
+         *
+         * @param associations all the associations which have connected transports
+         */
+        void onTransportsChanged(@NonNull List<AssociationInfo> associations);
+    }
+
+    /**
+     * Register a listener for any changes to
+     * {@link com.android.server.companion.transport.Transport}. Your app will receive a callback to
+     * {@link OnTransportsChangedListener} immediately with all the existing transports.
+     *
+     * @hide
+     */
+    public void addOnTransportsChangedListener(
+            @NonNull Executor executor, @NonNull OnTransportsChangedListener listener) {
+        final OnTransportsChangedListenerProxy proxy = new OnTransportsChangedListenerProxy(
+                executor, listener);
+        try {
+            mService.addOnTransportsChangedListener(proxy);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Unregister a listener to stop receiving any changes to
+     * {@link com.android.server.companion.transport.Transport}.
+     *
+     * @hide
+     */
+    public void removeOnTransportsChangedListener(
+            @NonNull OnTransportsChangedListener listener) {
+        final OnTransportsChangedListenerProxy proxy = new OnTransportsChangedListenerProxy(
+                null, listener);
+        try {
+            mService.removeOnTransportsChangedListener(proxy);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Send a message to remote devices
+     *
+     * @hide
+     */
+    public void sendMessage(int messageType, byte[] data, int[] associationIds) {
+        try {
+            mService.sendMessage(messageType, data, associationIds);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Listener when a message is received for the registered message type
+     *
+     * @see #addOnMessageReceivedListener(Executor, int, OnMessageReceivedListener)
+     *
+     * @hide
+     */
+    public interface OnMessageReceivedListener {
+        /**
+         * Called when a message is received
+         */
+        void onMessageReceived(int associationId, byte[] data);
+    }
+
+    /**
+     * Register a listener to receive callbacks when a message is received by the given type
+     *
+     * @see com.android.server.companion.transport.Transport for supported message types
+     *
+     * @hide
+     */
+    public void addOnMessageReceivedListener(@NonNull Executor executor, int messageType,
+            OnMessageReceivedListener listener) {
+        final OnMessageReceivedListenerProxy proxy = new OnMessageReceivedListenerProxy(
+                executor, listener);
+        try {
+            mService.addOnMessageReceivedListener(messageType, proxy);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Unregister a listener to stop receiving callbacks when a message is received by the given
+     * type
+     *
+     * @see com.android.server.companion.transport.Transport for supported message types
+     *
+     * @hide
+     */
+    public void removeOnMessageReceivedListener(int messageType,
+            OnMessageReceivedListener listener) {
+        final OnMessageReceivedListenerProxy proxy = new OnMessageReceivedListenerProxy(
+                null, listener);
+        try {
+            mService.removeOnMessageReceivedListener(messageType, proxy);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Checks whether the bluetooth device represented by the mac address was recently associated
      * with the companion app. This allows these devices to skip the Bluetooth pairing dialog if
      * their pairing variant is {@link BluetoothDevice#PAIRING_VARIANT_CONSENT}.
@@ -1277,6 +1390,40 @@
         }
     }
 
+    private static class OnTransportsChangedListenerProxy
+            extends IOnTransportsChangedListener.Stub {
+        private final Executor mExecutor;
+        private final OnTransportsChangedListener mListener;
+
+        private OnTransportsChangedListenerProxy(Executor executor,
+                OnTransportsChangedListener listener) {
+            mExecutor = executor;
+            mListener = listener;
+        }
+
+        @Override
+        public void onTransportsChanged(@NonNull List<AssociationInfo> associations) {
+            mExecutor.execute(() -> mListener.onTransportsChanged(associations));
+        }
+    }
+
+    private static class OnMessageReceivedListenerProxy
+            extends IOnMessageReceivedListener.Stub {
+        private final Executor mExecutor;
+        private final OnMessageReceivedListener mListener;
+
+        private OnMessageReceivedListenerProxy(Executor executor,
+                OnMessageReceivedListener listener) {
+            mExecutor = executor;
+            mListener = listener;
+        }
+
+        @Override
+        public void onMessageReceived(int associationId, byte[] data) {
+            mExecutor.execute(() -> mListener.onMessageReceived(associationId, data));
+        }
+    }
+
     private static class SystemDataTransferCallbackProxy extends ISystemDataTransferCallback.Stub {
         private final Executor mExecutor;
         private final OutcomeReceiver<Void, CompanionException> mCallback;
diff --git a/core/java/android/companion/ICompanionDeviceManager.aidl b/core/java/android/companion/ICompanionDeviceManager.aidl
index cb4baca..b5e2670 100644
--- a/core/java/android/companion/ICompanionDeviceManager.aidl
+++ b/core/java/android/companion/ICompanionDeviceManager.aidl
@@ -19,6 +19,8 @@
 import android.app.PendingIntent;
 import android.companion.IAssociationRequestCallback;
 import android.companion.IOnAssociationsChangedListener;
+import android.companion.IOnMessageReceivedListener;
+import android.companion.IOnTransportsChangedListener;
 import android.companion.ISystemDataTransferCallback;
 import android.companion.AssociationInfo;
 import android.companion.AssociationRequest;
@@ -67,6 +69,16 @@
 
     void removeOnAssociationsChangedListener(IOnAssociationsChangedListener listener, int userId);
 
+    void addOnTransportsChangedListener(IOnTransportsChangedListener listener);
+
+    void removeOnTransportsChangedListener(IOnTransportsChangedListener listener);
+
+    void sendMessage(int messageType, in byte[] data, in int[] associationIds);
+
+    void addOnMessageReceivedListener(int messageType, IOnMessageReceivedListener listener);
+
+    void removeOnMessageReceivedListener(int messageType, IOnMessageReceivedListener listener);
+
     void notifyDeviceAppeared(int associationId);
 
     void notifyDeviceDisappeared(int associationId);
diff --git a/core/java/android/companion/IOnMessageReceivedListener.aidl b/core/java/android/companion/IOnMessageReceivedListener.aidl
new file mode 100644
index 0000000..17f03f8
--- /dev/null
+++ b/core/java/android/companion/IOnMessageReceivedListener.aidl
@@ -0,0 +1,23 @@
+/*
+ * 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 per  missions and
+ * limitations under the License.
+ */
+
+package android.companion;
+
+/** @hide */
+interface IOnMessageReceivedListener {
+
+    oneway void onMessageReceived(int associationId, in byte[] data);
+}
\ No newline at end of file
diff --git a/core/java/android/companion/IOnTransportsChangedListener.aidl b/core/java/android/companion/IOnTransportsChangedListener.aidl
new file mode 100644
index 0000000..a101476
--- /dev/null
+++ b/core/java/android/companion/IOnTransportsChangedListener.aidl
@@ -0,0 +1,25 @@
+/*
+ * 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 per  missions and
+ * limitations under the License.
+ */
+
+package android.companion;
+
+import android.companion.AssociationInfo;
+
+/** @hide */
+interface IOnTransportsChangedListener {
+
+    oneway void onTransportsChanged(in List<AssociationInfo> associations);
+}
\ No newline at end of file
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index 6cc4c8a..90681cb 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -545,12 +545,13 @@
                 @VirtualDisplayFlag int flags,
                 @Nullable @CallbackExecutor Executor executor,
                 @Nullable VirtualDisplay.Callback callback) {
-            VirtualDisplayConfig config = new VirtualDisplayConfig.Builder(
+            VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(
                     getVirtualDisplayName(), width, height, densityDpi)
-                    .setSurface(surface)
-                    .setFlags(flags)
-                    .build();
-            return createVirtualDisplay(config, executor, callback);
+                    .setFlags(flags);
+            if (surface != null) {
+                builder.setSurface(surface);
+            }
+            return createVirtualDisplay(builder.build(), executor, callback);
         }
 
         /**
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 50dd7a0..6ae71d2 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -1409,9 +1409,10 @@
      * @param hdrConversionMode The {@link HdrConversionMode} to set.
      * Note, {@code HdrConversionMode.preferredHdrOutputType} is only applicable when
      * {@code HdrConversionMode.conversionMode} is {@link HdrConversionMode#HDR_CONVERSION_FORCE}.
+     * If {@code HdrConversionMode.preferredHdrOutputType} is not set in case when
+     * {@code HdrConversionMode.conversionMode} is {@link HdrConversionMode#HDR_CONVERSION_FORCE},
+     * it means that preferred output type is SDR.
      *
-     * @throws IllegalArgumentException if hdrConversionMode.preferredHdrOutputType is not set
-     * when hdrConversionMode.conversionMode is {@link HdrConversionMode#HDR_CONVERSION_FORCE}.
      * @throws IllegalArgumentException if hdrConversionMode.preferredHdrOutputType is set but
      * hdrConversionMode.conversionMode is not {@link HdrConversionMode#HDR_CONVERSION_FORCE}.
      *
diff --git a/core/java/android/hardware/display/VirtualDisplayConfig.java b/core/java/android/hardware/display/VirtualDisplayConfig.java
index 6b56a06..067ae4d 100644
--- a/core/java/android/hardware/display/VirtualDisplayConfig.java
+++ b/core/java/android/hardware/display/VirtualDisplayConfig.java
@@ -27,13 +27,13 @@
 import android.os.Handler;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.ArraySet;
 import android.view.Display;
 import android.view.Surface;
 
-import java.util.ArrayList;
 import java.util.Collections;
-import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 
 /**
  * Holds configuration used to create {@link VirtualDisplay} instances.
@@ -51,8 +51,8 @@
     private final Surface mSurface;
     private final String mUniqueId;
     private final int mDisplayIdToMirror;
-    private final boolean mWindowManagerMirroring;
-    private ArrayList<String> mDisplayCategories = null;
+    private final boolean mWindowManagerMirroringEnabled;
+    private ArraySet<String> mDisplayCategories = null;
     private final float mRequestedRefreshRate;
 
     private VirtualDisplayConfig(
@@ -64,8 +64,8 @@
             @Nullable Surface surface,
             @Nullable String uniqueId,
             int displayIdToMirror,
-            boolean windowManagerMirroring,
-            @NonNull ArrayList<String> displayCategories,
+            boolean windowManagerMirroringEnabled,
+            @NonNull ArraySet<String> displayCategories,
             float requestedRefreshRate) {
         mName = name;
         mWidth = width;
@@ -75,7 +75,7 @@
         mSurface = surface;
         mUniqueId = uniqueId;
         mDisplayIdToMirror = displayIdToMirror;
-        mWindowManagerMirroring = windowManagerMirroring;
+        mWindowManagerMirroringEnabled = windowManagerMirroringEnabled;
         mDisplayCategories = displayCategories;
         mRequestedRefreshRate = requestedRefreshRate;
     }
@@ -151,8 +151,8 @@
      * if DisplayManager should record contents instead.
      * @hide
      */
-    public boolean isWindowManagerMirroring() {
-        return mWindowManagerMirroring;
+    public boolean isWindowManagerMirroringEnabled() {
+        return mWindowManagerMirroringEnabled;
     }
 
     /**
@@ -161,8 +161,8 @@
      * @see Builder#setDisplayCategories
      */
     @NonNull
-    public List<String> getDisplayCategories() {
-        return Collections.unmodifiableList(mDisplayCategories);
+    public Set<String> getDisplayCategories() {
+        return Collections.unmodifiableSet(mDisplayCategories);
     }
 
     /**
@@ -185,8 +185,8 @@
         dest.writeTypedObject(mSurface, flags);
         dest.writeString8(mUniqueId);
         dest.writeInt(mDisplayIdToMirror);
-        dest.writeBoolean(mWindowManagerMirroring);
-        dest.writeStringList(mDisplayCategories);
+        dest.writeBoolean(mWindowManagerMirroringEnabled);
+        dest.writeArraySet(mDisplayCategories);
         dest.writeFloat(mRequestedRefreshRate);
     }
 
@@ -210,7 +210,7 @@
                 && Objects.equals(mSurface, that.mSurface)
                 && Objects.equals(mUniqueId, that.mUniqueId)
                 && mDisplayIdToMirror == that.mDisplayIdToMirror
-                && mWindowManagerMirroring == that.mWindowManagerMirroring
+                && mWindowManagerMirroringEnabled == that.mWindowManagerMirroringEnabled
                 && Objects.equals(mDisplayCategories, that.mDisplayCategories)
                 && mRequestedRefreshRate == that.mRequestedRefreshRate;
     }
@@ -219,7 +219,7 @@
     public int hashCode() {
         int hashCode = Objects.hash(
                 mName, mWidth, mHeight, mDensityDpi, mFlags, mSurface, mUniqueId,
-                mDisplayIdToMirror, mWindowManagerMirroring, mDisplayCategories,
+                mDisplayIdToMirror, mWindowManagerMirroringEnabled, mDisplayCategories,
                 mRequestedRefreshRate);
         return hashCode;
     }
@@ -236,7 +236,7 @@
                 + " mSurface=" + mSurface
                 + " mUniqueId=" + mUniqueId
                 + " mDisplayIdToMirror=" + mDisplayIdToMirror
-                + " mWindowManagerMirroring=" + mWindowManagerMirroring
+                + " mWindowManagerMirroringEnabled=" + mWindowManagerMirroringEnabled
                 + " mDisplayCategories=" + mDisplayCategories
                 + " mRequestedRefreshRate=" + mRequestedRefreshRate
                 + ")";
@@ -251,9 +251,8 @@
         mSurface = in.readTypedObject(Surface.CREATOR);
         mUniqueId = in.readString8();
         mDisplayIdToMirror = in.readInt();
-        mWindowManagerMirroring = in.readBoolean();
-        mDisplayCategories = new ArrayList<>();
-        in.readStringList(mDisplayCategories);
+        mWindowManagerMirroringEnabled = in.readBoolean();
+        mDisplayCategories = (ArraySet<String>) in.readArraySet(null);
         mRequestedRefreshRate = in.readFloat();
     }
 
@@ -283,8 +282,8 @@
         private Surface mSurface = null;
         private String mUniqueId = null;
         private int mDisplayIdToMirror = DEFAULT_DISPLAY;
-        private boolean mWindowManagerMirroring = false;
-        private ArrayList<String> mDisplayCategories = new ArrayList<>();
+        private boolean mWindowManagerMirroringEnabled = false;
+        private ArraySet<String> mDisplayCategories = new ArraySet<>();
         private float mRequestedRefreshRate = 0.0f;
 
         /**
@@ -370,8 +369,8 @@
          * @hide
          */
         @NonNull
-        public Builder setWindowManagerMirroring(boolean windowManagerMirroring) {
-            mWindowManagerMirroring = windowManagerMirroring;
+        public Builder setWindowManagerMirroringEnabled(boolean windowManagerMirroringEnabled) {
+            mWindowManagerMirroringEnabled = windowManagerMirroringEnabled;
             return this;
         }
 
@@ -383,7 +382,7 @@
          * {@link android.content.pm.ActivityInfo#requiredDisplayCategory}.
          */
         @NonNull
-        public Builder setDisplayCategories(@NonNull List<String> displayCategories) {
+        public Builder setDisplayCategories(@NonNull Set<String> displayCategories) {
             mDisplayCategories.clear();
             mDisplayCategories.addAll(Objects.requireNonNull(displayCategories));
             return this;
@@ -435,7 +434,7 @@
                     mSurface,
                     mUniqueId,
                     mDisplayIdToMirror,
-                    mWindowManagerMirroring,
+                    mWindowManagerMirroringEnabled,
                     mDisplayCategories,
                     mRequestedRefreshRate);
         }
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 26bc5d12..259012f 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -60,7 +60,6 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
@@ -181,9 +180,6 @@
 
     private final ArrayMap<IBinder, IWallpaperEngineWrapper> mActiveEngines = new ArrayMap<>();
 
-    private Handler mBackgroundHandler;
-    private HandlerThread mBackgroundThread;
-
     static final class WallpaperCommand {
         String action;
         int x;
@@ -202,6 +198,14 @@
      */
     public class Engine {
         IWallpaperEngineWrapper mIWallpaperEngine;
+        final ArraySet<RectF> mLocalColorAreas = new ArraySet<>(4);
+        final ArraySet<RectF> mLocalColorsToAdd = new ArraySet<>(4);
+
+        // 2D matrix [x][y] to represent a page of a portion of a window
+        EngineWindowPage[] mWindowPages = new EngineWindowPage[0];
+        Bitmap mLastScreenshot;
+        int mLastWindowPage = -1;
+        private boolean mResetWindowPages;
 
         // Copies from mIWallpaperEngine.
         HandlerCaller mCaller;
@@ -262,27 +266,11 @@
 
         final Object mLock = new Object();
         boolean mOffsetMessageEnqueued;
-
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
         float mPendingXOffset;
         float mPendingYOffset;
         float mPendingXOffsetStep;
         float mPendingYOffsetStep;
-
-        /**
-         * local color extraction related fields
-         * to be used by the background thread only (except the atomic boolean)
-         */
-        final ArraySet<RectF> mLocalColorAreas = new ArraySet<>(4);
-        final ArraySet<RectF> mLocalColorsToAdd = new ArraySet<>(4);
-        private long mLastProcessLocalColorsTimestamp;
-        private AtomicBoolean mProcessLocalColorsPending = new AtomicBoolean(false);
-        private int mPixelCopyCount = 0;
-        // 2D matrix [x][y] to represent a page of a portion of a window
-        EngineWindowPage[] mWindowPages = new EngineWindowPage[0];
-        Bitmap mLastScreenshot;
-        private boolean mResetWindowPages;
-
         boolean mPendingSync;
         MotionEvent mPendingMove;
         boolean mIsInAmbientMode;
@@ -291,8 +279,12 @@
         private long mLastColorInvalidation;
         private final Runnable mNotifyColorsChanged = this::notifyColorsChanged;
 
+        // used to throttle processLocalColors
+        private long mLastProcessLocalColorsTimestamp;
+        private AtomicBoolean mProcessLocalColorsPending = new AtomicBoolean(false);
         private final Supplier<Long> mClockFunction;
         private final Handler mHandler;
+
         private Display mDisplay;
         private Context mDisplayContext;
         private int mDisplayState;
@@ -862,7 +854,7 @@
                             + "was not established.");
                 }
                 mResetWindowPages = true;
-                processLocalColors();
+                processLocalColors(mPendingXOffset, mPendingXOffsetStep);
             } catch (RemoteException e) {
                 Log.w(TAG, "Can't notify system because wallpaper connection was lost.", e);
             }
@@ -1400,7 +1392,7 @@
                             resetWindowPages();
                             mSession.finishDrawing(mWindow, null /* postDrawTransaction */,
                                                    Integer.MAX_VALUE);
-                            processLocalColors();
+                            processLocalColors(mPendingXOffset, mPendingXOffsetStep);
                         }
                         reposition();
                         reportEngineShown(shouldWaitForEngineShown());
@@ -1544,7 +1536,7 @@
             if (!mDestroyed) {
                 mVisible = visible;
                 reportVisibility(false);
-                if (mReportedVisible) processLocalColors();
+                if (mReportedVisible) processLocalColors(mPendingXOffset, mPendingXOffsetStep);
             } else {
                 AnimationHandler.requestAnimatorsEnabled(visible, this);
             }
@@ -1647,41 +1639,31 @@
             }
 
             // setup local color extraction data
-            processLocalColors();
+            processLocalColors(xOffset, xOffsetStep);
         }
 
         /**
          * Thread-safe util to call {@link #processLocalColorsInternal} with a minimum interval of
          * {@link #PROCESS_LOCAL_COLORS_INTERVAL_MS} between two calls.
          */
-        private void processLocalColors() {
+        private void processLocalColors(float xOffset, float xOffsetStep) {
             if (mProcessLocalColorsPending.compareAndSet(false, true)) {
                 final long now = mClockFunction.get();
                 final long timeSinceLastColorProcess = now - mLastProcessLocalColorsTimestamp;
                 final long timeToWait = Math.max(0,
                         PROCESS_LOCAL_COLORS_INTERVAL_MS - timeSinceLastColorProcess);
 
-                mBackgroundHandler.postDelayed(() -> {
+                mHandler.postDelayed(() -> {
                     mLastProcessLocalColorsTimestamp = now + timeToWait;
                     mProcessLocalColorsPending.set(false);
-                    processLocalColorsInternal();
+                    processLocalColorsInternal(xOffset, xOffsetStep);
                 }, timeToWait);
             }
         }
 
-        private void processLocalColorsInternal() {
+        private void processLocalColorsInternal(float xOffset, float xOffsetStep) {
             // implemented by the wallpaper
             if (supportsLocalColorExtraction()) return;
-            assertBackgroundThread();
-            float xOffset;
-            float xOffsetStep;
-            float wallpaperDimAmount;
-            synchronized (mLock) {
-                xOffset = mPendingXOffset;
-                xOffsetStep = mPendingXOffsetStep;
-                wallpaperDimAmount = mWallpaperDimAmount;
-            }
-
             if (DEBUG) {
                 Log.d(TAG, "processLocalColors " + xOffset + " of step "
                         + xOffsetStep);
@@ -1744,7 +1726,7 @@
                 xPage = mWindowPages.length - 1;
             }
             current = mWindowPages[xPage];
-            updatePage(current, xPage, xPages, wallpaperDimAmount);
+            updatePage(current, xPage, xPages, finalXOffsetStep);
             Trace.endSection();
         }
 
@@ -1764,23 +1746,16 @@
             }
         }
 
-        /**
-         * Must be called with the surface lock held.
-         * Must not be called if the surface is not valid.
-         * Will unlock the surface when done using it.
-         */
         void updatePage(EngineWindowPage currentPage, int pageIndx, int numPages,
-                float wallpaperDimAmount) {
-
-            assertBackgroundThread();
-
+                float xOffsetStep) {
             // in case the clock is zero, we start with negative time
             long current = SystemClock.elapsedRealtime() - DEFAULT_UPDATE_SCREENSHOT_DURATION;
             long lapsed = current - currentPage.getLastUpdateTime();
             // Always update the page when the last update time is <= 0
             // This is important especially when the device first boots
-            if (lapsed < DEFAULT_UPDATE_SCREENSHOT_DURATION) return;
-
+            if (lapsed < DEFAULT_UPDATE_SCREENSHOT_DURATION) {
+                return;
+            }
             Surface surface = mSurfaceHolder.getSurface();
             if (!surface.isValid()) return;
             boolean widthIsLarger = mSurfaceSize.x > mSurfaceSize.y;
@@ -1796,42 +1771,33 @@
             Bitmap screenShot = Bitmap.createBitmap(width, height,
                     Bitmap.Config.ARGB_8888);
             final Bitmap finalScreenShot = screenShot;
-            final String pixelCopySectionName = "WallpaperService#pixelCopy";
-            final int pixelCopyCount = mPixelCopyCount++;
-            Trace.beginAsyncSection(pixelCopySectionName, pixelCopyCount);
-            try {
-                PixelCopy.request(surface, screenShot, (res) -> {
-                    Trace.endAsyncSection(pixelCopySectionName, pixelCopyCount);
-                    if (DEBUG) Log.d(TAG, "result of pixel copy is " + res);
-                    if (res != PixelCopy.SUCCESS) {
-                        Bitmap lastBitmap = currentPage.getBitmap();
-                        // assign the last bitmap taken for now
-                        currentPage.setBitmap(mLastScreenshot);
-                        Bitmap lastScreenshot = mLastScreenshot;
-                        if (lastScreenshot != null && !lastScreenshot.isRecycled()
-                                && !Objects.equals(lastBitmap, lastScreenshot)) {
-                            updatePageColors(currentPage, pageIndx, numPages, wallpaperDimAmount);
-                        }
-                    } else {
-                        mLastScreenshot = finalScreenShot;
-                        // going to hold this lock for a while
-                        currentPage.setBitmap(finalScreenShot);
-                        currentPage.setLastUpdateTime(current);
-                        updatePageColors(currentPage, pageIndx, numPages, wallpaperDimAmount);
+            Trace.beginSection("WallpaperService#pixelCopy");
+            PixelCopy.request(surface, screenShot, (res) -> {
+                Trace.endSection();
+                if (DEBUG) Log.d(TAG, "result of pixel copy is " + res);
+                if (res != PixelCopy.SUCCESS) {
+                    Bitmap lastBitmap = currentPage.getBitmap();
+                    // assign the last bitmap taken for now
+                    currentPage.setBitmap(mLastScreenshot);
+                    Bitmap lastScreenshot = mLastScreenshot;
+                    if (lastScreenshot != null && !lastScreenshot.isRecycled()
+                            && !Objects.equals(lastBitmap, lastScreenshot)) {
+                        updatePageColors(currentPage, pageIndx, numPages, xOffsetStep);
                     }
-                }, mBackgroundHandler);
-            } catch (IllegalArgumentException e) {
-                // this can potentially happen if the surface is invalidated right between the
-                // surface.isValid() check and the PixelCopy operation.
-                // in this case, stop: we'll compute colors on the next processLocalColors call.
-                Log.i(TAG, "Cancelling processLocalColors: exception caught during PixelCopy");
-            }
+                } else {
+                    mLastScreenshot = finalScreenShot;
+                    // going to hold this lock for a while
+                    currentPage.setBitmap(finalScreenShot);
+                    currentPage.setLastUpdateTime(current);
+                    updatePageColors(currentPage, pageIndx, numPages, xOffsetStep);
+                }
+            }, mHandler);
+
         }
         // locked by the passed page
-        private void updatePageColors(
-                EngineWindowPage page, int pageIndx, int numPages, float wallpaperDimAmount) {
+        private void updatePageColors(EngineWindowPage page, int pageIndx, int numPages,
+                float xOffsetStep) {
             if (page.getBitmap() == null) return;
-            assertBackgroundThread();
             Trace.beginSection("WallpaperService#updatePageColors");
             if (DEBUG) {
                 Log.d(TAG, "updatePageColorsLocked for page " + pageIndx + " with areas "
@@ -1853,7 +1819,7 @@
                     Log.e(TAG, "Error creating page local color bitmap", e);
                     continue;
                 }
-                WallpaperColors color = WallpaperColors.fromBitmap(target, wallpaperDimAmount);
+                WallpaperColors color = WallpaperColors.fromBitmap(target, mWallpaperDimAmount);
                 target.recycle();
                 WallpaperColors currentColor = page.getColors(area);
 
@@ -1870,26 +1836,17 @@
                                 + " local color callback for area" + area + " for page " + pageIndx
                                 + " of " + numPages);
                     }
-                    mHandler.post(() -> {
-                        try {
-                            mConnection.onLocalWallpaperColorsChanged(area, color,
-                                    mDisplayContext.getDisplayId());
-                        } catch (RemoteException e) {
-                            Log.e(TAG, "Error calling Connection.onLocalWallpaperColorsChanged", e);
-                        }
-                    });
+                    try {
+                        mConnection.onLocalWallpaperColorsChanged(area, color,
+                                mDisplayContext.getDisplayId());
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Error calling Connection.onLocalWallpaperColorsChanged", e);
+                    }
                 }
             }
             Trace.endSection();
         }
 
-        private void assertBackgroundThread() {
-            if (!mBackgroundHandler.getLooper().isCurrentThread()) {
-                throw new IllegalStateException(
-                        "ProcessLocalColors should be called from the background thread");
-            }
-        }
-
         private RectF generateSubRect(RectF in, int pageInx, int numPages) {
             float minLeft = (float) (pageInx) / (float) (numPages);
             float maxRight = (float) (pageInx + 1) / (float) (numPages);
@@ -1914,6 +1871,7 @@
             if (supportsLocalColorExtraction()) return;
             if (!mResetWindowPages) return;
             mResetWindowPages = false;
+            mLastWindowPage = -1;
             for (int i = 0; i < mWindowPages.length; i++) {
                 mWindowPages[i].setLastUpdateTime(0L);
             }
@@ -1939,10 +1897,12 @@
             if (DEBUG) {
                 Log.d(TAG, "addLocalColorsAreas adding local color areas " + regions);
             }
-            mBackgroundHandler.post(() -> {
+            mHandler.post(() -> {
                 mLocalColorsToAdd.addAll(regions);
-                processLocalColors();
+                processLocalColors(mPendingXOffset, mPendingYOffset);
             });
+
+
         }
 
         /**
@@ -1952,7 +1912,7 @@
          */
         public void removeLocalColorsAreas(@NonNull List<RectF> regions) {
             if (supportsLocalColorExtraction()) return;
-            mBackgroundHandler.post(() -> {
+            mHandler.post(() -> {
                 float step = mPendingXOffsetStep;
                 mLocalColorsToAdd.removeAll(regions);
                 mLocalColorAreas.removeAll(regions);
@@ -2618,9 +2578,6 @@
     @Override
     public void onCreate() {
         Trace.beginSection("WPMS.onCreate");
-        mBackgroundThread = new HandlerThread("DefaultWallpaperLocalColorExtractor");
-        mBackgroundThread.start();
-        mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
         super.onCreate();
         Trace.endSection();
     }
@@ -2633,7 +2590,6 @@
             engineWrapper.destroy();
         }
         mActiveEngines.clear();
-        mBackgroundThread.quitSafely();
         Trace.endSection();
     }
 
diff --git a/core/java/android/text/GraphemeClusterSegmentFinder.java b/core/java/android/text/GraphemeClusterSegmentFinder.java
index 656774f..0f6fdaf 100644
--- a/core/java/android/text/GraphemeClusterSegmentFinder.java
+++ b/core/java/android/text/GraphemeClusterSegmentFinder.java
@@ -18,7 +18,8 @@
 
 import android.annotation.IntRange;
 import android.annotation.NonNull;
-import android.graphics.Paint;
+import android.graphics.TemporaryBuffer;
+import android.graphics.text.GraphemeBreak;
 
 /**
  * Implementation of {@code SegmentFinder} using grapheme clusters as the text segment. Whitespace
@@ -31,8 +32,8 @@
  *     Segmentation - Grapheme Cluster Boundaries</a>
  */
 public class GraphemeClusterSegmentFinder extends SegmentFinder {
-    private final CharSequence mText;
-    private final TextPaint mTextPaint;
+    private static AutoGrowArray.FloatArray sTempAdvances = null;
+    private final boolean[] mIsGraphemeBreak;
 
     /**
      * Constructs a GraphemeClusterSegmentFinder instance for the specified text which uses the
@@ -43,51 +44,73 @@
      */
     public GraphemeClusterSegmentFinder(
             @NonNull CharSequence text, @NonNull TextPaint textPaint) {
-        mText = text;
-        mTextPaint = textPaint;
+
+        if (sTempAdvances == null) {
+            sTempAdvances = new AutoGrowArray.FloatArray(text.length());
+        } else if (sTempAdvances.size() < text.length()) {
+            sTempAdvances.resize(text.length());
+        }
+
+        mIsGraphemeBreak = new boolean[text.length()];
+        float[] advances = sTempAdvances.getRawArray();
+        char[] chars = TemporaryBuffer.obtain(text.length());
+
+        TextUtils.getChars(text, 0, text.length(), chars, 0);
+
+        textPaint.getTextWidths(chars, 0, text.length(), advances);
+
+        GraphemeBreak.isGraphemeBreak(advances, chars, /* start= */ 0, /* end= */ text.length(),
+                mIsGraphemeBreak);
+        TemporaryBuffer.recycle(chars);
+    }
+
+    private int previousBoundary(@IntRange(from = 0) int offset) {
+        if (offset <= 0) return DONE;
+        do {
+            --offset;
+        } while (offset > 0 && !mIsGraphemeBreak[offset]);
+        return offset;
+    }
+
+    private int nextBoundary(@IntRange(from = 0) int offset) {
+        if (offset >= mIsGraphemeBreak.length) return DONE;
+        do {
+            ++offset;
+        } while (offset < mIsGraphemeBreak.length && !mIsGraphemeBreak[offset]);
+        return offset;
     }
 
     @Override
     public int previousStartBoundary(@IntRange(from = 0) int offset) {
-        if (offset == 0) return DONE;
-        int boundary = mTextPaint.getTextRunCursor(
-                mText, 0, mText.length(), false, offset, Paint.CURSOR_BEFORE);
-        return boundary == -1 ? DONE : boundary;
+        return previousBoundary(offset);
     }
 
     @Override
     public int previousEndBoundary(@IntRange(from = 0) int offset) {
         if (offset == 0) return DONE;
-        int boundary = mTextPaint.getTextRunCursor(
-                mText, 0, mText.length(), false, offset, Paint.CURSOR_BEFORE);
+        int boundary = previousBoundary(offset);
         // Check that there is another cursor position before, otherwise this is not a valid
         // end boundary.
-        if (mTextPaint.getTextRunCursor(
-                mText, 0, mText.length(), false, boundary, Paint.CURSOR_BEFORE) == -1) {
+        if (boundary == DONE || previousBoundary(boundary) == DONE) {
             return DONE;
         }
-        return boundary == -1 ? DONE : boundary;
+        return boundary;
     }
 
     @Override
     public int nextStartBoundary(@IntRange(from = 0) int offset) {
-        if (offset == mText.length()) return DONE;
-        int boundary = mTextPaint.getTextRunCursor(
-                mText, 0, mText.length(), false, offset, Paint.CURSOR_AFTER);
+        if (offset == mIsGraphemeBreak.length) return DONE;
+        int boundary = nextBoundary(offset);
         // Check that there is another cursor position after, otherwise this is not a valid
         // start boundary.
-        if (mTextPaint.getTextRunCursor(
-                mText, 0, mText.length(), false, boundary, Paint.CURSOR_AFTER) == -1) {
+        if (boundary == DONE || nextBoundary(boundary) == DONE) {
             return DONE;
         }
-        return boundary == -1 ? DONE : boundary;
+        return boundary;
     }
 
     @Override
     public int nextEndBoundary(@IntRange(from = 0) int offset) {
-        if (offset == mText.length()) return DONE;
-        int boundary = mTextPaint.getTextRunCursor(
-                mText, 0, mText.length(), false, offset, Paint.CURSOR_AFTER);
-        return boundary == -1 ? DONE : boundary;
+        return nextBoundary(offset);
     }
 }
diff --git a/core/java/android/view/MotionPredictor.java b/core/java/android/view/MotionPredictor.java
index 4d32efe..7d452f9 100644
--- a/core/java/android/view/MotionPredictor.java
+++ b/core/java/android/view/MotionPredictor.java
@@ -17,14 +17,11 @@
 package android.view;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 
 import libcore.util.NativeAllocationRegistry;
 
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
 /**
  * Calculate motion predictions.
  *
@@ -68,9 +65,12 @@
 
     /**
      * Record a movement so that in the future, a prediction for the current gesture can be
-     * generated. Ensure to add all motions from the gesture of interest to generate correct
-     * predictions.
+     * generated. Only gestures from one input device at a time should be provided to an instance of
+     * MotionPredictor.
+     *
      * @param event The received event
+     *
+     * @throws IllegalArgumentException if an inconsistent MotionEvent stream is sent.
      */
     public void record(@NonNull MotionEvent event) {
         if (!isPredictionEnabled()) {
@@ -80,28 +80,24 @@
     }
 
     /**
-     * Get predicted events for all gestures that have been provided to {@link #record}.
-     * If events from multiple devices were sent to 'record', this will produce a separate
-     * {@link MotionEvent} for each device. The returned list may be empty if no predictions for
-     * any of the added events/devices are available.
+     * Get a predicted event for the gesture that has been provided to {@link #record}.
      * Predictions may not reach the requested timestamp if the confidence in the prediction results
      * is low.
      *
      * @param predictionTimeNanos The time that the prediction should target, in the
      * {@link android.os.SystemClock#uptimeMillis} time base, but in nanoseconds.
      *
-     * @return A list of predicted motion events, with at most one for each device observed by
-     * {@link #record}. Be sure to check the historical data in addition to the latest
-     * ({@link MotionEvent#getX getX()}, {@link MotionEvent#getY getY()}) coordinates for smooth
-     * prediction curves. An empty list is returned if predictions are not supported, or not
-     * possible for the current set of gestures.
+     * @return The predicted motion event, or `null` if predictions are not supported, or not
+     * possible for the current gesture. Be sure to check the historical data in addition to the
+     * latest ({@link MotionEvent#getX getX()}, {@link MotionEvent#getY getY()}) coordinates for
+     * smooth prediction curves.
      */
-    @NonNull
-    public List<MotionEvent> predict(long predictionTimeNanos) {
+    @Nullable
+    public MotionEvent predict(long predictionTimeNanos) {
         if (!isPredictionEnabled()) {
-            return Collections.emptyList();
+            return null;
         }
-        return Arrays.asList(nativePredict(mPtr, predictionTimeNanos));
+        return nativePredict(mPtr, predictionTimeNanos);
     }
 
     private boolean isPredictionEnabled() {
@@ -129,7 +125,7 @@
 
     private static native long nativeInitialize(int offsetNanos);
     private static native void nativeRecord(long nativePtr, MotionEvent event);
-    private static native MotionEvent[] nativePredict(long nativePtr, long predictionTimeNanos);
+    private static native MotionEvent nativePredict(long nativePtr, long predictionTimeNanos);
     private static native boolean nativeIsPredictionAvailable(long nativePtr, int deviceId,
             int source);
     private static native long nativeGetNativeMotionPredictorFinalizer();
diff --git a/core/java/com/android/internal/app/procstats/UidState.java b/core/java/com/android/internal/app/procstats/UidState.java
index 8761b74..4911346 100644
--- a/core/java/com/android/internal/app/procstats/UidState.java
+++ b/core/java/com/android/internal/app/procstats/UidState.java
@@ -150,6 +150,7 @@
     public void resetSafely(long now) {
         mDurations.resetTable();
         mStartTime = now;
+        mProcesses.removeIf(p -> !p.isInUse());
     }
 
     /**
diff --git a/core/jni/android_view_MotionPredictor.cpp b/core/jni/android_view_MotionPredictor.cpp
index 2c232fa..de3e81c 100644
--- a/core/jni/android_view_MotionPredictor.cpp
+++ b/core/jni/android_view_MotionPredictor.cpp
@@ -20,7 +20,6 @@
 #include <input/MotionPredictor.h>
 
 #include "android_view_MotionEvent.h"
-#include "core_jni_converters.h"
 #include "core_jni_helpers.h"
 
 /**
@@ -30,14 +29,6 @@
 
 namespace android {
 
-// ----------------------------------------------------------------------------
-
-static struct {
-    jclass clazz;
-} gMotionEventClassInfo;
-
-// ----------------------------------------------------------------------------
-
 static void release(void* ptr) {
     delete reinterpret_cast<MotionPredictor*>(ptr);
 }
@@ -57,14 +48,20 @@
                                                       jobject event) {
     MotionPredictor* predictor = reinterpret_cast<MotionPredictor*>(ptr);
     MotionEvent* motionEvent = android_view_MotionEvent_getNativePtr(env, event);
-    predictor->record(*motionEvent);
+
+    android::base::Result<void> result = predictor->record(*motionEvent);
+    if (!result.ok()) {
+        jniThrowException(env, "java/lang/IllegalArgumentException",
+                          result.error().message().c_str());
+    }
 }
 
 static jobject android_view_MotionPredictor_nativePredict(JNIEnv* env, jclass clazz, jlong ptr,
                                                           jlong predictionTimeNanos) {
     MotionPredictor* predictor = reinterpret_cast<MotionPredictor*>(ptr);
-    return toJavaArray(env, predictor->predict(static_cast<nsecs_t>(predictionTimeNanos)),
-                       gMotionEventClassInfo.clazz, &android_view_MotionEvent_obtainFromNative);
+    return android_view_MotionEvent_obtainFromNative(env,
+                                                     predictor->predict(static_cast<nsecs_t>(
+                                                             predictionTimeNanos)));
 }
 
 static jboolean android_view_MotionPredictor_nativeIsPredictionAvailable(JNIEnv* env, jclass clazz,
@@ -84,15 +81,13 @@
          (void*)android_view_MotionPredictor_nativeGetNativeMotionPredictorFinalizer},
         {"nativeRecord", "(JLandroid/view/MotionEvent;)V",
          (void*)android_view_MotionPredictor_nativeRecord},
-        {"nativePredict", "(JJ)[Landroid/view/MotionEvent;",
+        {"nativePredict", "(JJ)Landroid/view/MotionEvent;",
          (void*)android_view_MotionPredictor_nativePredict},
         {"nativeIsPredictionAvailable", "(JII)Z",
          (void*)android_view_MotionPredictor_nativeIsPredictionAvailable},
 }};
 
 int register_android_view_MotionPredictor(JNIEnv* env) {
-    jclass motionEventClazz = FindClassOrDie(env, "android/view/MotionEvent");
-    gMotionEventClassInfo.clazz = MakeGlobalRefOrDie(env, motionEventClazz);
     return RegisterMethodsOrDie(env, "android/view/MotionPredictor", gMotionPredictorMethods.data(),
                                 gMotionPredictorMethods.size());
 }
diff --git a/core/jni/android_window_WindowInfosListener.cpp b/core/jni/android_window_WindowInfosListener.cpp
index 32b85b0..f2cbe8a 100644
--- a/core/jni/android_window_WindowInfosListener.cpp
+++ b/core/jni/android_window_WindowInfosListener.cpp
@@ -118,7 +118,6 @@
     }
 
     ~WindowInfosListener() override {
-        SurfaceComposerClient::getDefault()->removeWindowInfosListener(this);
         JNIEnv* env = AndroidRuntime::getJNIEnv();
         env->DeleteWeakGlobalRef(mListener);
     }
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index ab7b0ab..ed612a0 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -142,6 +142,7 @@
         optional int64 finish_clock_time_ms = 4;
     }
     repeated BroadcastSummary historical_broadcasts_summary = 6;
+    repeated BroadcastRecordProto pending_broadcasts = 7;
 }
 
 message MemInfoDumpProto {
@@ -439,6 +440,7 @@
 
         optional int32 id = 1;
         optional .android.app.NotificationProto notification = 2;
+        optional int32 foregroundServiceType = 3;
     }
     optional Foreground foreground = 13;
 
diff --git a/core/tests/coretests/src/com/android/internal/app/procstats/ProcessStatsTest.java b/core/tests/coretests/src/com/android/internal/app/procstats/ProcessStatsTest.java
index 9b9a84b..35b3267 100644
--- a/core/tests/coretests/src/com/android/internal/app/procstats/ProcessStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/procstats/ProcessStatsTest.java
@@ -156,4 +156,19 @@
                         eq(0),
                         eq(APP_1_PROCESS_NAME));
     }
+
+    @SmallTest
+    public void testSafelyResetClearsProcessInUidState() throws Exception {
+        ProcessStats processStats = new ProcessStats();
+        ProcessState processState =
+                processStats.getProcessStateLocked(
+                        APP_1_PACKAGE_NAME, APP_1_UID, APP_1_VERSION, APP_1_PROCESS_NAME);
+        processState.makeActive();
+        UidState uidState = processStats.mUidStates.get(APP_1_UID);
+        assertTrue(uidState.isInUse());
+        processState.makeInactive();
+        uidState.resetSafely(NOW_MS);
+        processState.makeActive();
+        assertFalse(uidState.isInUse());
+    }
 }
diff --git a/graphics/java/android/graphics/text/GraphemeBreak.java b/graphics/java/android/graphics/text/GraphemeBreak.java
new file mode 100644
index 0000000..f82b2fd
--- /dev/null
+++ b/graphics/java/android/graphics/text/GraphemeBreak.java
@@ -0,0 +1,59 @@
+/*
+ * 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.graphics.text;
+
+/** @hide */
+public class GraphemeBreak {
+    private GraphemeBreak() { }
+
+    /**
+     * Util method that checks if the offsets in given range are grapheme break.
+     *
+     * @param advances the advances of characters in the given text. It contains the font
+     *                information used by the algorithm to determine the grapheme break. It's useful
+     *                 when some character is missing in the font. For example, if the smile emoji
+     *                 "0xD83D 0xDE0A" is not found in the font and is displayed as 2 characters.
+     *                 We can't treat it as a single grapheme cluster.
+     * @param text the text to be processed.
+     * @param start the start offset of the queried range, inclusive.
+     * @param end the end offset of the queried range, exclusive.
+     * @param isGraphemeBreak the array to receive the result. The i-th element of the
+     *                       array will be set to true if the offset (start + i) is a grapheme
+     *                       break; otherwise, it will be set to false.
+     */
+    public static void isGraphemeBreak(float[] advances, char[] text, int start, int end,
+            boolean[] isGraphemeBreak) {
+        if (start > end) {
+            throw new IllegalArgumentException("start is greater than end: start = " + start
+                    + " end = " + end);
+        }
+        if (advances.length < end) {
+            throw new IllegalArgumentException("the length of advances is less than end"
+                    + "advances.length = " + advances.length
+                    + " end = " + end);
+        }
+        if (isGraphemeBreak.length < end - start) {
+            throw new IndexOutOfBoundsException("isGraphemeBreak doesn't have enough space to "
+                    + "receive the result, isGraphemeBreak.length: " + isGraphemeBreak.length
+                    + " needed space: " + (end - start));
+        }
+        nIsGraphemeBreak(advances, text, start, end, isGraphemeBreak);
+    }
+
+    private static native void nIsGraphemeBreak(float[] advances, char[] text, int start, int end,
+            boolean[] isGraphemeBreak);
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 2252587..70d3b35 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -204,7 +204,7 @@
     // and exit, since exit itself can trigger a number of changes that update the stages.
     private boolean mShouldUpdateRecents;
     private boolean mExitSplitScreenOnHide;
-    private boolean mIsSplitEntering;
+    private boolean mIsDividerRemoteAnimating;
     private boolean mIsDropEntering;
     private boolean mIsExiting;
 
@@ -881,7 +881,7 @@
 
         // Set false to avoid record new bounds with old task still on top;
         mShouldUpdateRecents = false;
-        mIsSplitEntering = true;
+        mIsDividerRemoteAnimating = true;
         if (mSplitRequest == null) {
             mSplitRequest = new SplitRequest(mainTaskId,
                     mainPendingIntent != null ? mainPendingIntent.getIntent() : null,
@@ -974,7 +974,7 @@
     }
 
     private void onRemoteAnimationFinishedOrCancelled(WindowContainerTransaction evictWct) {
-        mIsSplitEntering = false;
+        mIsDividerRemoteAnimating = false;
         mShouldUpdateRecents = true;
         mSplitRequest = null;
         // If any stage has no child after animation finished, it means that split will display
@@ -1247,7 +1247,7 @@
             }
         });
         mShouldUpdateRecents = false;
-        mIsSplitEntering = false;
+        mIsDividerRemoteAnimating = false;
 
         mSplitLayout.getInvisibleBounds(mTempRect1);
         if (childrenToTop == null || childrenToTop.getTopVisibleChildTaskId() == INVALID_TASK_ID) {
@@ -1590,7 +1590,7 @@
                 && !ENABLE_SHELL_TRANSITIONS) {
             // Clear the divider remote animating flag as the divider will be re-rendered to apply
             // the new rotation config.
-            mIsSplitEntering = false;
+            mIsDividerRemoteAnimating = false;
             mSplitLayout.update(null /* t */);
             onLayoutSizeChanged(mSplitLayout);
         }
@@ -1640,9 +1640,9 @@
     }
 
     void onChildTaskAppeared(StageListenerImpl stageListener, int taskId) {
+        // Handle entering split screen while there is a split pair running in the background.
         if (stageListener == mSideStageListener && !isSplitScreenVisible() && isSplitActive()
-                && !mIsSplitEntering) {
-            // Handle entring split case here if split already running background.
+                && mSplitRequest == null) {
             if (mIsDropEntering) {
                 mSplitLayout.resetDividerPosition();
             } else {
@@ -1734,7 +1734,7 @@
         mDividerVisible = visible;
         sendSplitVisibilityChanged();
 
-        if (mIsSplitEntering) {
+        if (mIsDividerRemoteAnimating) {
             ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
                     "   Skip animating divider bar due to it's remote animating.");
             return;
@@ -1754,7 +1754,7 @@
                     "   Skip animating divider bar due to divider leash not ready.");
             return;
         }
-        if (mIsSplitEntering) {
+        if (mIsDividerRemoteAnimating) {
             ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
                     "   Skip animating divider bar due to it's remote animating.");
             return;
@@ -1822,7 +1822,8 @@
                 mSplitLayout.flingDividerToDismiss(
                         mSideStagePosition != SPLIT_POSITION_BOTTOM_OR_RIGHT,
                         EXIT_REASON_APP_FINISHED);
-            } else if (!isSplitScreenVisible() && !mIsSplitEntering) {
+            } else if (!isSplitScreenVisible() && mSplitRequest == null) {
+                // Dismiss split screen in the background once any sides of the split become empty.
                 exitSplitScreen(null /* childrenToTop */, EXIT_REASON_APP_FINISHED);
             }
         } else if (isSideStage && hasChildren && !mMainStage.isActive()) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index ef405c8..d1565d1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -78,7 +78,6 @@
         int mAnimType = 0;
         final IBinder mTransition;
 
-        Transitions.TransitionFinishCallback mFinishCallback = null;
         Transitions.TransitionHandler mLeftoversHandler = null;
         WindowContainerTransaction mFinishWCT = null;
 
@@ -241,20 +240,25 @@
         }
         if (pipChange == null) {
             if (mixed.mLeftoversHandler != null) {
-                return mixed.mLeftoversHandler.startAnimation(mixed.mTransition, info,
-                        startTransaction, finishTransaction, finishCallback);
+                if (mixed.mLeftoversHandler.startAnimation(mixed.mTransition,
+                        info, startTransaction, finishTransaction, (wct, wctCB) -> {
+                            mActiveTransitions.remove(mixed);
+                            finishCallback.onTransitionFinished(wct, wctCB);
+                        })) {
+                    return true;
+                }
             }
+            mActiveTransitions.remove(mixed);
             return false;
         }
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Splitting PIP into a separate"
                         + " animation because remote-animation likely doesn't support it");
-        mixed.mFinishCallback = finishCallback;
         Transitions.TransitionFinishCallback finishCB = (wct, wctCB) -> {
             --mixed.mInFlightSubAnimations;
             mixed.joinFinishArgs(wct, wctCB);
             if (mixed.mInFlightSubAnimations > 0) return;
             mActiveTransitions.remove(mixed);
-            mixed.mFinishCallback.onTransitionFinished(mixed.mFinishWCT, wctCB);
+            finishCallback.onTransitionFinished(mixed.mFinishWCT, wctCB);
         };
         // Split the transition into 2 parts: the pip part and the rest.
         mixed.mInFlightSubAnimations = 2;
@@ -304,10 +308,10 @@
         }
         if (pipChange == null) {
             // um, something probably went wrong.
+            mActiveTransitions.remove(mixed);
             return false;
         }
         final boolean isGoingHome = homeIsOpening;
-        mixed.mFinishCallback = finishCallback;
         Transitions.TransitionFinishCallback finishCB = (wct, wctCB) -> {
             --mixed.mInFlightSubAnimations;
             mixed.joinFinishArgs(wct, wctCB);
@@ -316,7 +320,7 @@
             if (isGoingHome) {
                 mSplitHandler.onTransitionAnimationComplete();
             }
-            mixed.mFinishCallback.onTransitionFinished(mixed.mFinishWCT, wctCB);
+            finishCallback.onTransitionFinished(mixed.mFinishWCT, wctCB);
         };
         if (isGoingHome) {
             ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animation is actually mixed "
@@ -408,7 +412,6 @@
         unlinkMissingParents(everythingElse);
         final MixedTransition mixed = new MixedTransition(
                 MixedTransition.TYPE_DISPLAY_AND_SPLIT_CHANGE, transition);
-        mixed.mFinishCallback = finishCallback;
         mActiveTransitions.add(mixed);
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animation is a mix of display change "
                 + "and split change.");
@@ -420,7 +423,7 @@
             mixed.joinFinishArgs(wct, wctCB);
             if (mixed.mInFlightSubAnimations > 0) return;
             mActiveTransitions.remove(mixed);
-            mixed.mFinishCallback.onTransitionFinished(mixed.mFinishWCT, null /* wctCB */);
+            finishCallback.onTransitionFinished(mixed.mFinishWCT, null /* wctCB */);
         };
 
         // Dispatch the display change. This will most-likely be taken by the default handler.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
index 8d29901..bcc37ba 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
@@ -143,8 +143,12 @@
         Animation a = null;
         if (animAttr != 0) {
             if (overrideType == ANIM_FROM_STYLE && !isTask) {
-                a = loadCustomActivityTransition(animAttr, options, enter, transitionAnimation);
-                if (a == null) {
+                final TransitionInfo.AnimationOptions.CustomActivityTransition customTransition =
+                        getCustomActivityTransition(animAttr, options);
+                if (customTransition != null) {
+                    a = loadCustomActivityTransition(
+                            customTransition, options, enter, transitionAnimation);
+                } else {
                     a = transitionAnimation
                             .loadAnimationAttr(options.getPackageName(), options.getAnimations(),
                                     animAttr, translucent);
@@ -161,10 +165,8 @@
         return a;
     }
 
-    static Animation loadCustomActivityTransition(int animAttr,
-            TransitionInfo.AnimationOptions options, boolean enter,
-            TransitionAnimation transitionAnimation) {
-        Animation a = null;
+    static TransitionInfo.AnimationOptions.CustomActivityTransition getCustomActivityTransition(
+            int animAttr, TransitionInfo.AnimationOptions options) {
         boolean isOpen = false;
         switch (animAttr) {
             case R.styleable.WindowAnimation_activityOpenEnterAnimation:
@@ -178,17 +180,19 @@
                 return null;
         }
 
-        final TransitionInfo.AnimationOptions.CustomActivityTransition transitionAnim =
-                options.getCustomActivityTransition(isOpen);
-        if (transitionAnim != null) {
-            a = transitionAnimation.loadAppTransitionAnimation(options.getPackageName(),
-                    enter ? transitionAnim.getCustomEnterResId()
-                            : transitionAnim.getCustomExitResId());
-            if (a != null && transitionAnim.getCustomBackgroundColor() != 0) {
-                a.setBackdropColor(transitionAnim.getCustomBackgroundColor());
-            }
-        }
+        return options.getCustomActivityTransition(isOpen);
+    }
 
+    static Animation loadCustomActivityTransition(
+            @NonNull TransitionInfo.AnimationOptions.CustomActivityTransition transitionAnim,
+            TransitionInfo.AnimationOptions options, boolean enter,
+            TransitionAnimation transitionAnimation) {
+        final Animation a = transitionAnimation.loadAppTransitionAnimation(options.getPackageName(),
+                enter ? transitionAnim.getCustomEnterResId()
+                        : transitionAnim.getCustomExitResId());
+        if (a != null && transitionAnim.getCustomBackgroundColor() != 0) {
+            a.setBackdropColor(transitionAnim.getCustomBackgroundColor());
+        }
         return a;
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 3b154d1..b39b953 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -772,6 +772,12 @@
                 ++mergeIdx;
                 continue;
             }
+            if (mergeCandidate.mInfo == null) {
+                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition merge candidate"
+                        + " %s is not ready yet", mergeCandidate.mToken);
+                // The later transition should not be merged if the prior one is not ready.
+                return;
+            }
             if (mergeCandidate.mMerged) {
                 throw new IllegalStateException("Can't merge a transition after not-merging"
                         + " a preceding one.");
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index 6c9b186..e63bbeb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -563,6 +563,33 @@
         assertEquals(0, mDefaultHandler.activeCount());
     }
 
+
+    @Test
+    public void testTransitionMergingOnFinish() {
+        final Transitions transitions = createTestTransitions();
+        transitions.replaceDefaultHandlerForTest(mDefaultHandler);
+
+        // The current transition.
+        final IBinder transitToken1 = new Binder();
+        requestStartTransition(transitions, transitToken1);
+        onTransitionReady(transitions, transitToken1);
+
+        // The next ready transition.
+        final IBinder transitToken2 = new Binder();
+        requestStartTransition(transitions, transitToken2);
+        onTransitionReady(transitions, transitToken2);
+
+        // The non-ready merge candidate.
+        final IBinder transitTokenNotReady = new Binder();
+        requestStartTransition(transitions, transitTokenNotReady);
+
+        mDefaultHandler.setSimulateMerge(true);
+        mDefaultHandler.mFinishes.get(0).onTransitionFinished(null /* wct */, null /* wctCB */);
+
+        // Make sure that the non-ready transition is not merged.
+        assertEquals(0, mDefaultHandler.mergeCount());
+    }
+
     @Test
     public void testTransitionOrderMatchesCore() {
         Transitions transitions = createTestTransitions();
@@ -1036,6 +1063,21 @@
         }
     }
 
+    private static void requestStartTransition(Transitions transitions, IBinder token) {
+        transitions.requestStartTransition(token,
+                new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */));
+    }
+
+    private static void onTransitionReady(Transitions transitions, IBinder token) {
+        transitions.onTransitionReady(token, createTransitionInfo(),
+                mock(SurfaceControl.Transaction.class), mock(SurfaceControl.Transaction.class));
+    }
+
+    private static TransitionInfo createTransitionInfo() {
+        return new TransitionInfoBuilder(TRANSIT_OPEN)
+                .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
+    }
+
     private static SurfaceControl createMockSurface(boolean valid) {
         SurfaceControl sc = mock(SurfaceControl.class);
         doReturn(valid).when(sc).isValid();
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 9c967a2..7228b89 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -375,6 +375,7 @@
         "jni/text/LineBreaker.cpp",
         "jni/text/MeasuredText.cpp",
         "jni/text/TextShaper.cpp",
+        "jni/text/GraphemeBreak.cpp",
     ],
 
     header_libs: [
diff --git a/libs/hwui/apex/LayoutlibLoader.cpp b/libs/hwui/apex/LayoutlibLoader.cpp
index b7a1563..770822a 100644
--- a/libs/hwui/apex/LayoutlibLoader.cpp
+++ b/libs/hwui/apex/LayoutlibLoader.cpp
@@ -66,6 +66,7 @@
 extern int register_android_graphics_text_LineBreaker(JNIEnv* env);
 extern int register_android_graphics_text_MeasuredText(JNIEnv* env);
 extern int register_android_graphics_text_TextShaper(JNIEnv* env);
+extern int register_android_graphics_text_GraphemeBreak(JNIEnv* env);
 
 extern int register_android_util_PathParser(JNIEnv* env);
 extern int register_android_view_DisplayListCanvas(JNIEnv* env);
@@ -125,6 +126,8 @@
         {"android.graphics.text.MeasuredText",
          REG_JNI(register_android_graphics_text_MeasuredText)},
         {"android.graphics.text.TextRunShaper", REG_JNI(register_android_graphics_text_TextShaper)},
+        {"android.graphics.text.GraphemeBreak",
+         REG_JNI(register_android_graphics_text_GraphemeBreak)},
         {"android.util.PathParser", REG_JNI(register_android_util_PathParser)},
 };
 
diff --git a/libs/hwui/apex/jni_runtime.cpp b/libs/hwui/apex/jni_runtime.cpp
index c509ed4..09ae7e7 100644
--- a/libs/hwui/apex/jni_runtime.cpp
+++ b/libs/hwui/apex/jni_runtime.cpp
@@ -77,6 +77,7 @@
 extern int register_android_graphics_text_MeasuredText(JNIEnv* env);
 extern int register_android_graphics_text_LineBreaker(JNIEnv *env);
 extern int register_android_graphics_text_TextShaper(JNIEnv *env);
+extern int register_android_graphics_text_GraphemeBreak(JNIEnv* env);
 extern int register_android_graphics_MeshSpecification(JNIEnv* env);
 extern int register_android_graphics_Mesh(JNIEnv* env);
 
@@ -148,6 +149,7 @@
             REG_JNI(register_android_graphics_text_MeasuredText),
             REG_JNI(register_android_graphics_text_LineBreaker),
             REG_JNI(register_android_graphics_text_TextShaper),
+            REG_JNI(register_android_graphics_text_GraphemeBreak),
             REG_JNI(register_android_graphics_MeshSpecification),
             REG_JNI(register_android_graphics_Mesh),
 
diff --git a/libs/hwui/jni/text/GraphemeBreak.cpp b/libs/hwui/jni/text/GraphemeBreak.cpp
new file mode 100644
index 0000000..55f03bd
--- /dev/null
+++ b/libs/hwui/jni/text/GraphemeBreak.cpp
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "GraphemeBreaker"
+
+#include <minikin/GraphemeBreak.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
+
+#include "GraphicsJNI.h"
+
+namespace android {
+
+static void nIsGraphemeBreak(JNIEnv* env, jclass, jfloatArray advances, jcharArray text, jint start,
+                             jint end, jbooleanArray isGraphemeBreak) {
+    if (start > end || env->GetArrayLength(advances) < end ||
+        env->GetArrayLength(isGraphemeBreak) < end - start) {
+        doThrowAIOOBE(env);
+    }
+
+    if (start == end) {
+        return;
+    }
+
+    ScopedFloatArrayRO advancesArray(env, advances);
+    ScopedCharArrayRO textArray(env, text);
+    ScopedBooleanArrayRW isGraphemeBreakArray(env, isGraphemeBreak);
+
+    size_t count = end - start;
+    for (size_t offset = 0; offset < count; ++offset) {
+        bool isBreak = minikin::GraphemeBreak::isGraphemeBreak(advancesArray.get(), textArray.get(),
+                                                               start, end, start + offset);
+        isGraphemeBreakArray[offset] = isBreak ? JNI_TRUE : JNI_FALSE;
+    }
+}
+
+static const JNINativeMethod gMethods[] = {
+        {"nIsGraphemeBreak",
+         "("
+         "[F"  // advances
+         "[C"  // text
+         "I"   // start
+         "I"   // end
+         "[Z"  // isGraphemeBreak
+         ")V",
+         (void*)nIsGraphemeBreak},
+};
+
+int register_android_graphics_text_GraphemeBreak(JNIEnv* env) {
+    return RegisterMethodsOrDie(env, "android/graphics/text/GraphemeBreak", gMethods,
+                                NELEM(gMethods));
+}
+
+}  // namespace android
diff --git a/media/java/android/media/projection/MediaProjection.java b/media/java/android/media/projection/MediaProjection.java
index 9e9012e..d70e8b3 100644
--- a/media/java/android/media/projection/MediaProjection.java
+++ b/media/java/android/media/projection/MediaProjection.java
@@ -191,7 +191,7 @@
             } else {
                 session = ContentRecordingSession.createTaskSession(launchCookie);
             }
-            virtualDisplayConfig.setWindowManagerMirroring(true);
+            virtualDisplayConfig.setWindowManagerMirroringEnabled(true);
             final DisplayManager dm = mContext.getSystemService(DisplayManager.class);
             final VirtualDisplay virtualDisplay = dm.createVirtualDisplay(this,
                     virtualDisplayConfig.build(), callback, handler, windowContext);
diff --git a/media/java/android/media/projection/OWNERS b/media/java/android/media/projection/OWNERS
index 96532d0..2273f81 100644
--- a/media/java/android/media/projection/OWNERS
+++ b/media/java/android/media/projection/OWNERS
@@ -1,3 +1,4 @@
 michaelwr@google.com
 santoscordon@google.com
 chaviw@google.com
+nmusgrave@google.com
diff --git a/packages/CredentialManager/res/values/strings.xml b/packages/CredentialManager/res/values/strings.xml
index e12eff9..f655d6b 100644
--- a/packages/CredentialManager/res/values/strings.xml
+++ b/packages/CredentialManager/res/values/strings.xml
@@ -110,7 +110,7 @@
   <!-- This is a label for a button that takes user to the next screen. [CHAR LIMIT=20] -->
   <string name="get_dialog_button_label_continue">Continue</string>
   <!-- Separator for sign-in type and username in a sign-in entry. -->
-  <string name="get_dialog_sign_in_type_username_separator" translatable="false">" - "</string>
+  <string name="get_dialog_sign_in_type_username_separator" translatable="false">" • "</string>
   <!-- This text is followed by a list of one or more options. [CHAR LIMIT=80] -->
   <string name="get_dialog_title_sign_in_options">Sign-in options</string>
   <!-- Column heading for displaying sign-ins for a specific username. [CHAR LIMIT=80] -->
diff --git a/packages/CredentialManager/res/values/themes.xml b/packages/CredentialManager/res/values/themes.xml
index 82aebb7..428c85a 100644
--- a/packages/CredentialManager/res/values/themes.xml
+++ b/packages/CredentialManager/res/values/themes.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
-  <style name="Theme.CredentialSelector" parent="@android:style/ThemeOverlay.Material">
+  <style name="Theme.CredentialSelector" parent="@*android:style/ThemeOverlay.DeviceDefault.Accent.DayNight">
     <item name="android:windowContentOverlay">@null</item>
     <item name="android:windowNoTitle">true</item>
     <item name="android:windowBackground">@android:color/transparent</item>
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
index a2d1e4d..b32fe3f 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
@@ -42,11 +42,13 @@
 import com.android.credentialmanager.createflow.EnabledProviderInfo
 import com.android.credentialmanager.createflow.RequestDisplayInfo
 import com.android.credentialmanager.getflow.GetCredentialUiState
+import com.android.credentialmanager.getflow.findAutoSelectEntry
 import androidx.credentials.CreateCredentialRequest.DisplayInfo
 import androidx.credentials.CreatePublicKeyCredentialRequest
 import androidx.credentials.CreatePasswordRequest
 import androidx.credentials.GetPasswordOption
 import androidx.credentials.GetPublicKeyCredentialOption
+import com.android.credentialmanager.common.ProviderActivityState
 
 import java.time.Instant
 
@@ -128,10 +130,20 @@
                     getCredentialUiState = null,
                 )
             }
-            RequestInfo.TYPE_GET -> UiState(
-                createCredentialUiState = null,
-                getCredentialUiState = getCredentialInitialUiState(originName)!!,
-            )
+            RequestInfo.TYPE_GET -> {
+                val getCredentialInitialUiState = getCredentialInitialUiState(originName)!!
+                val autoSelectEntry =
+                    findAutoSelectEntry(getCredentialInitialUiState.providerDisplayInfo)
+                UiState(
+                    createCredentialUiState = null,
+                    getCredentialUiState = getCredentialInitialUiState,
+                    selectedEntry = autoSelectEntry,
+                    providerActivityState =
+                    if (autoSelectEntry == null) ProviderActivityState.NOT_APPLICABLE
+                    else ProviderActivityState.READY_TO_LAUNCH,
+                    isAutoSelectFlow = autoSelectEntry != null,
+                )
+            }
             else -> throw IllegalStateException("Unrecognized request type: ${requestInfo.type}")
         }
     }
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
index a5c74943..ca30c53 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
@@ -44,6 +44,9 @@
     val selectedEntry: BaseEntry? = null,
     val providerActivityState: ProviderActivityState = ProviderActivityState.NOT_APPLICABLE,
     val dialogState: DialogState = DialogState.ACTIVE,
+    // True if the UI has one and onely one auto selectable entry. Its provider activiey will be
+    // launched immediately, and canceling it will cancel the whole UI flow.
+    val isAutoSelectFlow: Boolean = false,
 )
 
 class CredentialSelectorViewModel(
@@ -96,13 +99,20 @@
         val resultCode = providerActivityResult.resultCode
         val resultData = providerActivityResult.data
         if (resultCode == Activity.RESULT_CANCELED) {
-            // Re-display the CredMan UI if the user canceled from the provider UI.
-            Log.d(Constants.LOG_TAG, "The provider activity was cancelled," +
-                " re-displaying our UI.")
-            uiState = uiState.copy(
-                selectedEntry = null,
-                providerActivityState = ProviderActivityState.NOT_APPLICABLE,
-            )
+            // Re-display the CredMan UI if the user canceled from the provider UI, or cancel
+            // the UI if this is the auto select flow.
+            if (uiState.isAutoSelectFlow) {
+                Log.d(Constants.LOG_TAG, "The auto selected provider activity was cancelled," +
+                    " ending the credential manager activity.")
+                onUserCancel()
+            } else {
+                Log.d(Constants.LOG_TAG, "The provider activity was cancelled," +
+                    " re-displaying our UI.")
+                uiState = uiState.copy(
+                    selectedEntry = null,
+                    providerActivityState = ProviderActivityState.NOT_APPLICABLE,
+                )
+            }
         } else {
             if (entry != null) {
                 Log.d(
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
index 43912c2..e61633f 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
@@ -149,7 +149,10 @@
                         icon = providerIcon,
                         displayName = providerLabel,
                         credentialEntryList = getCredentialOptionInfoList(
-                            it.providerFlattenedComponentName, it.credentialEntries, context
+                            providerId = it.providerFlattenedComponentName,
+                            providerLabel = providerLabel,
+                            credentialEntries = it.credentialEntries,
+                            context = context
                         ),
                         authenticationEntryList = getAuthenticationEntryList(
                             it.providerFlattenedComponentName,
@@ -202,6 +205,7 @@
          */
         private fun getCredentialOptionInfoList(
             providerId: String,
+            providerLabel: String,
             credentialEntries: List<Entry>,
             context: Context,
         ): List<CredentialEntryInfo> {
@@ -212,6 +216,7 @@
                     is PasswordCredentialEntry -> {
                         result.add(CredentialEntryInfo(
                             providerId = providerId,
+                            providerDisplayName = providerLabel,
                             entryKey = it.key,
                             entrySubkey = it.subkey,
                             pendingIntent = credentialEntry.pendingIntent,
@@ -221,12 +226,16 @@
                             userName = credentialEntry.username.toString(),
                             displayName = credentialEntry.displayName?.toString(),
                             icon = credentialEntry.icon.loadDrawable(context),
+                            shouldTintIcon = credentialEntry.isDefaultIcon ?: false,
                             lastUsedTimeMillis = credentialEntry.lastUsedTime,
+                            isAutoSelectable = credentialEntry.isAutoSelectAllowed &&
+                                credentialEntry.autoSelectAllowedFromOption,
                         ))
                     }
                     is PublicKeyCredentialEntry -> {
                         result.add(CredentialEntryInfo(
                             providerId = providerId,
+                            providerDisplayName = providerLabel,
                             entryKey = it.key,
                             entrySubkey = it.subkey,
                             pendingIntent = credentialEntry.pendingIntent,
@@ -236,12 +245,16 @@
                             userName = credentialEntry.username.toString(),
                             displayName = credentialEntry.displayName?.toString(),
                             icon = credentialEntry.icon.loadDrawable(context),
+                            shouldTintIcon = credentialEntry.isDefaultIcon,
                             lastUsedTimeMillis = credentialEntry.lastUsedTime,
+                            isAutoSelectable = credentialEntry.isAutoSelectAllowed &&
+                                credentialEntry.autoSelectAllowedFromOption,
                         ))
                     }
                     is CustomCredentialEntry -> {
                         result.add(CredentialEntryInfo(
                             providerId = providerId,
+                            providerDisplayName = providerLabel,
                             entryKey = it.key,
                             entrySubkey = it.subkey,
                             pendingIntent = credentialEntry.pendingIntent,
@@ -251,7 +264,10 @@
                             userName = credentialEntry.title.toString(),
                             displayName = credentialEntry.subtitle?.toString(),
                             icon = credentialEntry.icon.loadDrawable(context),
+                            shouldTintIcon = credentialEntry.isDefaultIcon,
                             lastUsedTimeMillis = credentialEntry.lastUsedTime,
+                            isAutoSelectable = credentialEntry.isAutoSelectAllowed &&
+                                credentialEntry.autoSelectAllowedFromOption,
                         ))
                     }
                     else -> Log.d(
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/TestUtils.kt b/packages/CredentialManager/src/com/android/credentialmanager/TestUtils.kt
index 75b12ff..82d6952 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/TestUtils.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/TestUtils.kt
@@ -155,21 +155,23 @@
             userName: String,
             userDisplayName: String?,
             lastUsedTime: Instant?,
+            isAutoSelectAllowed: Boolean = false,
         ): Entry {
-            val intent = Intent("com.androidauth.androidvault.CONFIRM_PASSWORD")
-                .setPackage("com.androidauth.androidvault")
-            intent.putExtra("provider_extra_sample", "testprovider")
-            val pendingIntent = PendingIntent.getActivity(
-                context, 1,
-                intent, (PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
-                or PendingIntent.FLAG_ONE_SHOT)
+            val intent = Intent(Settings.ACTION_SYNC_SETTINGS)
+            val pendingIntent =
+                PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
+            val candidateQueryData = Bundle()
+            candidateQueryData.putBoolean(
+                "androidx.credentials.BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED",
+                isAutoSelectAllowed
             )
             val passkeyEntry = PublicKeyCredentialEntry.Builder(
                 context,
                 userName,
                 pendingIntent,
-                BeginGetPublicKeyCredentialOption(Bundle(), "id", "requestjson")
-            ).setDisplayName(userDisplayName).setLastUsedTime(lastUsedTime).build()
+                BeginGetPublicKeyCredentialOption(candidateQueryData, "id", "requestjson")
+            ).setDisplayName(userDisplayName).setLastUsedTime(lastUsedTime)
+                .setAutoSelectAllowed(isAutoSelectAllowed).build()
             return Entry(key, subkey, passkeyEntry.slice, Intent())
         }
     }
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/BottomSheet.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/BottomSheet.kt
index 0cb2bb0..edc902e 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/BottomSheet.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/BottomSheet.kt
@@ -18,7 +18,6 @@
 
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.ColumnScope
-import androidx.compose.material3.MaterialTheme
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.ui.Modifier
@@ -27,6 +26,7 @@
 import com.android.credentialmanager.common.material.ModalBottomSheetValue
 import com.android.credentialmanager.common.material.rememberModalBottomSheetState
 import com.android.credentialmanager.ui.theme.EntryShape
+import com.android.credentialmanager.ui.theme.LocalAndroidColorScheme
 
 /** Draws a modal bottom sheet with the same styles and effects shared by various flows. */
 @Composable
@@ -39,9 +39,7 @@
         skipHalfExpanded = true
     )
     ModalBottomSheetLayout(
-        sheetBackgroundColor = MaterialTheme.colorScheme.surfaceColorAtElevation(
-            ElevationTokens.Level1
-        ),
+        sheetBackgroundColor = LocalAndroidColorScheme.current.colorSurfaceBright,
         modifier = Modifier.background(Color.Transparent),
         sheetState = state,
         sheetContent = sheetContent,
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Cards.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Cards.kt
index c535268..3976f9a 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Cards.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Cards.kt
@@ -25,13 +25,13 @@
 import androidx.compose.foundation.lazy.LazyListScope
 import androidx.compose.material3.Card
 import androidx.compose.material3.CardDefaults
-import androidx.compose.material3.MaterialTheme
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.unit.dp
 import com.android.credentialmanager.ui.theme.Shapes
+import com.android.credentialmanager.ui.theme.LocalAndroidColorScheme
 
 /**
  * Container card for the whole sheet.
@@ -50,9 +50,7 @@
         modifier = modifier.fillMaxWidth().wrapContentHeight(),
         border = null,
         colors = CardDefaults.cardColors(
-            containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(
-                ElevationTokens.Level1
-            ),
+            containerColor = LocalAndroidColorScheme.current.colorSurfaceBright,
         ),
     ) {
         if (topAppBar != null) {
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ColorScheme.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ColorScheme.kt
deleted file mode 100644
index b2489fd..0000000
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ColorScheme.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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.credentialmanager.common.ui
-
-import androidx.compose.material3.ColorScheme
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.compositeOver
-import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.dp
-import kotlin.math.ln
-
-fun ColorScheme.surfaceColorAtElevation(elevation: Dp): Color {
-    if (elevation == 0.dp) return surface
-    val alpha = ((4.5f * ln(elevation.value + 1)) + 2f) / 100f
-    return surfaceTint.copy(alpha = alpha).compositeOver(surface)
-}
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ElevationTokens.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ElevationTokens.kt
deleted file mode 100644
index e1e666e..0000000
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ElevationTokens.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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.credentialmanager.common.ui
-
-import androidx.compose.ui.unit.dp
-
-/** Copied from androidx.compose.material3.tokens. */
-internal object ElevationTokens {
-    val Level0 = 0.0.dp
-    val Level1 = 1.0.dp
-    val Level2 = 3.0.dp
-    val Level3 = 6.0.dp
-    val Level4 = 8.0.dp
-    val Level5 = 12.0.dp
-}
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt
index 1e2a280..c0c29bb 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt
@@ -53,6 +53,7 @@
 import androidx.compose.ui.unit.dp
 import com.android.credentialmanager.R
 import com.android.credentialmanager.ui.theme.EntryShape
+import com.android.credentialmanager.ui.theme.LocalAndroidColorScheme
 import com.android.credentialmanager.ui.theme.Shapes
 
 @Composable
@@ -73,6 +74,7 @@
     passwordValue: String? = null,
     /** If true, draws a trailing lock icon. */
     isLockedAuthEntry: Boolean = false,
+    enforceOneLine: Boolean = false,
 ) {
     val iconPadding = Modifier.wrapContentSize().padding(
         // Horizontal padding should be 16dp, but the suggestion chip itself
@@ -92,12 +94,17 @@
                     // has 8dp horizontal elements padding
                     horizontal = 8.dp, vertical = 16.dp,
                 ),
+                // Make sure the trailing icon and text column are centered vertically.
                 verticalAlignment = Alignment.CenterVertically,
             ) {
-                Column(modifier = Modifier.wrapContentSize()) {
-                    SmallTitleText(entryHeadlineText)
+                // Apply weight so that the trailing icon can always show.
+                Column(modifier = Modifier.wrapContentHeight().fillMaxWidth().weight(1f)) {
+                    SmallTitleText(text = entryHeadlineText, enforceOneLine = enforceOneLine)
                     if (passwordValue != null) {
-                        Row(modifier = Modifier.fillMaxWidth()) {
+                        Row(
+                            modifier = Modifier.fillMaxWidth(),
+                            verticalAlignment = Alignment.CenterVertically,
+                        ) {
                             val visualTransformation = remember { PasswordVisualTransformation() }
                             val originalPassword by remember {
                                 mutableStateOf(passwordValue)
@@ -109,9 +116,10 @@
                                     ).text.text
                                 )
                             }
-                            BodySmallText(displayedPassword.value)
+                            BodySmallText(
+                                text = displayedPassword.value, enforceOneLine = enforceOneLine)
                             ToggleVisibilityButton(
-                                modifier = Modifier.padding(start = 5.dp).size(24.dp),
+                                modifier = Modifier.padding(start = 12.dp, top = 5.dp).size(24.dp),
                                 onToggle = {
                                     if (it) {
                                         displayedPassword.value = originalPassword
@@ -124,14 +132,14 @@
                             )
                         }
                     } else if (entrySecondLineText != null) {
-                        BodySmallText(entrySecondLineText)
+                        BodySmallText(text = entrySecondLineText, enforceOneLine = enforceOneLine)
                     }
                     if (entryThirdLineText != null) {
-                        BodySmallText(entryThirdLineText)
+                        BodySmallText(text = entryThirdLineText, enforceOneLine = enforceOneLine)
                     }
                 }
                 if (isLockedAuthEntry) {
-                    Box(modifier = Modifier.wrapContentSize()) {
+                    Box(modifier = Modifier.wrapContentSize().padding(start = 16.dp)) {
                         Icon(
                             imageVector = Icons.Outlined.Lock,
                             // Decorative purpose only.
@@ -198,9 +206,7 @@
         },
         border = null,
         colors = SuggestionChipDefaults.suggestionChipColors(
-            containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(
-                ElevationTokens.Level3
-            ),
+            containerColor = LocalAndroidColorScheme.current.colorSurfaceContainerHigh,
             // TODO: remove?
             labelColor = MaterialTheme.colorScheme.onSurfaceVariant,
             iconContentColor = MaterialTheme.colorScheme.onSurfaceVariant,
@@ -323,7 +329,7 @@
                         contentDescription = stringResource(
                             R.string.accessibility_back_arrow_button
                         ),
-                        modifier = Modifier.size(16.dp),
+                        modifier = Modifier.size(24.dp),
                         tint = MaterialTheme.colorScheme.onSurfaceVariant,
                     )
                 }
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SystemUiControllerUtils.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SystemUiControllerUtils.kt
index 2f14584..a619523 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SystemUiControllerUtils.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SystemUiControllerUtils.kt
@@ -16,11 +16,11 @@
 
 package com.android.credentialmanager.common.ui
 
-import androidx.compose.material3.MaterialTheme
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.graphics.Color
 import com.android.compose.SystemUiController
 import com.android.credentialmanager.common.material.ModalBottomSheetDefaults
+import com.android.credentialmanager.ui.theme.LocalAndroidColorScheme
 
 @Composable
 fun setTransparentSystemBarsColor(sysUiController: SystemUiController) {
@@ -34,9 +34,7 @@
         darkIcons = false
     )
     sysUiController.setNavigationBarColor(
-        color = MaterialTheme.colorScheme.surfaceColorAtElevation(
-            ElevationTokens.Level1
-        ),
+        color = LocalAndroidColorScheme.current.colorSurfaceBright,
         darkIcons = false
     )
 }
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Texts.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Texts.kt
index 8af729e..22871bcb 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Texts.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Texts.kt
@@ -23,6 +23,7 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.text.style.TextOverflow
 
 /**
  * The headline for a screen. E.g. "Create a passkey for X", "Choose a saved sign-in for X".
@@ -57,12 +58,14 @@
  * Body-small typography; on-surface-variant color.
  */
 @Composable
-fun BodySmallText(text: String, modifier: Modifier = Modifier) {
+fun BodySmallText(text: String, modifier: Modifier = Modifier, enforceOneLine: Boolean = false) {
     Text(
         modifier = modifier.wrapContentSize(),
         text = text,
         color = MaterialTheme.colorScheme.onSurfaceVariant,
         style = MaterialTheme.typography.bodySmall,
+        overflow = TextOverflow.Ellipsis,
+        maxLines = if (enforceOneLine) 1 else Int.MAX_VALUE
     )
 }
 
@@ -83,12 +86,14 @@
  * Title-small typography; on-surface color.
  */
 @Composable
-fun SmallTitleText(text: String, modifier: Modifier = Modifier) {
+fun SmallTitleText(text: String, modifier: Modifier = Modifier, enforceOneLine: Boolean = false) {
     Text(
         modifier = modifier.wrapContentSize(),
         text = text,
         color = MaterialTheme.colorScheme.onSurface,
         style = MaterialTheme.typography.titleSmall,
+        overflow = TextOverflow.Ellipsis,
+        maxLines = if (enforceOneLine) 1 else Int.MAX_VALUE
     )
 }
 
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
index b83c593..9fe7899 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
@@ -1,4 +1,18 @@
-@file:OptIn(ExperimentalMaterial3Api::class)
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
 package com.android.credentialmanager.createflow
 
@@ -15,7 +29,6 @@
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.wrapContentHeight
 import androidx.compose.material3.Divider
-import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.outlined.NewReleases
@@ -637,6 +650,7 @@
         // This subtitle would never be null for create password
             requestDisplayInfo.subtitle ?: ""
         else null,
+        enforceOneLine = true,
     )
 }
 
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
index 9a826f2..ab947ae 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
@@ -182,12 +182,14 @@
                             CredentialEntryRow(
                                 credentialEntryInfo = it.sortedCredentialEntryList.first(),
                                 onEntrySelected = onEntrySelected,
+                                enforceOneLine = true,
                             )
                         }
                         authenticationEntryList.forEach {
                             AuthenticationEntryRow(
                                 authenticationEntryInfo = it,
                                 onEntrySelected = onEntrySelected,
+                                enforceOneLine = true,
                             )
                         }
                     } else if (usernameForCredentialSize < 4) {
@@ -195,12 +197,14 @@
                             CredentialEntryRow(
                                 credentialEntryInfo = it.sortedCredentialEntryList.first(),
                                 onEntrySelected = onEntrySelected,
+                                enforceOneLine = true,
                             )
                         }
                         authenticationEntryList.take(4 - usernameForCredentialSize).forEach {
                             AuthenticationEntryRow(
                                 authenticationEntryInfo = it,
                                 onEntrySelected = onEntrySelected,
+                                enforceOneLine = true,
                             )
                         }
                     } else {
@@ -208,6 +212,7 @@
                             CredentialEntryRow(
                                 credentialEntryInfo = it.sortedCredentialEntryList.first(),
                                 onEntrySelected = onEntrySelected,
+                                enforceOneLine = true,
                             )
                         }
                     }
@@ -402,10 +407,12 @@
 fun CredentialEntryRow(
     credentialEntryInfo: CredentialEntryInfo,
     onEntrySelected: (BaseEntry) -> Unit,
+    enforceOneLine: Boolean = false,
 ) {
     Entry(
         onClick = { onEntrySelected(credentialEntryInfo) },
         iconImageBitmap = credentialEntryInfo.icon?.toBitmap()?.asImageBitmap(),
+        shouldApplyIconImageBitmapTint = credentialEntryInfo.shouldTintIcon,
         // Fall back to iconPainter if iconImageBitmap isn't available
         iconPainter =
         if (credentialEntryInfo.icon == null) painterResource(R.drawable.ic_other_sign_in_24)
@@ -415,15 +422,17 @@
             credentialEntryInfo.credentialType == CredentialType.PASSWORD) {
             "••••••••••••"
         } else {
-            if (TextUtils.isEmpty(credentialEntryInfo.displayName))
-                credentialEntryInfo.credentialTypeDisplayName
-            else
-                credentialEntryInfo.credentialTypeDisplayName +
-                    stringResource(
-                        R.string.get_dialog_sign_in_type_username_separator
-                    ) +
-                    credentialEntryInfo.displayName
+            val itemsToDisplay = listOf(
+                credentialEntryInfo.displayName,
+                credentialEntryInfo.credentialTypeDisplayName,
+                credentialEntryInfo.providerDisplayName
+            ).filterNot(TextUtils::isEmpty)
+            if (itemsToDisplay.isEmpty()) null
+            else itemsToDisplay.joinToString(
+                separator = stringResource(R.string.get_dialog_sign_in_type_username_separator)
+            )
         },
+        enforceOneLine = enforceOneLine,
     )
 }
 
@@ -431,6 +440,7 @@
 fun AuthenticationEntryRow(
     authenticationEntryInfo: AuthenticationEntryInfo,
     onEntrySelected: (BaseEntry) -> Unit,
+    enforceOneLine: Boolean = false,
 ) {
     Entry(
         onClick = { onEntrySelected(authenticationEntryInfo) },
@@ -442,6 +452,7 @@
             else R.string.locked_credential_entry_label_subtext_tap_to_unlock
         ),
         isLockedAuthEntry = !authenticationEntryInfo.isUnlockedAndEmpty,
+        enforceOneLine = enforceOneLine,
     )
 }
 
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
index 9727d3f..263a632 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
@@ -41,6 +41,23 @@
             !state.requestDisplayInfo.preferImmediatelyAvailableCredentials)
 }
 
+internal fun findAutoSelectEntry(providerDisplayInfo: ProviderDisplayInfo): CredentialEntryInfo? {
+    if (providerDisplayInfo.authenticationEntryList.isNotEmpty()) {
+        return null
+    }
+    if (providerDisplayInfo.sortedUserNameToCredentialEntryList.size == 1) {
+        val entryList = providerDisplayInfo.sortedUserNameToCredentialEntryList.firstOrNull()
+            ?: return null
+        if (entryList.sortedCredentialEntryList.size == 1) {
+            val entry = entryList.sortedCredentialEntryList.firstOrNull() ?: return null
+            if (entry.isAutoSelectable) {
+                return entry
+            }
+        }
+    }
+    return null
+}
+
 data class ProviderInfo(
     /**
      * Unique id (component name) of this provider.
@@ -77,10 +94,13 @@
     val credentialType: CredentialType,
     /** Localized type value of this credential used for display purpose. */
     val credentialTypeDisplayName: String,
+    val providerDisplayName: String,
     val userName: String,
     val displayName: String?,
     val icon: Drawable?,
+    val shouldTintIcon: Boolean,
     val lastUsedTimeMillis: Instant?,
+    val isAutoSelectable: Boolean,
 ) : BaseEntry(
     providerId,
     entryKey,
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/AndroidColorScheme.kt b/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/AndroidColorScheme.kt
index 120e493..8928e18 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/AndroidColorScheme.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/AndroidColorScheme.kt
@@ -40,32 +40,8 @@
  * most of the colors in this class will be removed in favor of their M3 counterpart.
  */
 class AndroidColorScheme internal constructor(context: Context) {
-    val colorPrimary = getColor(context, R.attr.colorPrimary)
-    val colorPrimaryDark = getColor(context, R.attr.colorPrimaryDark)
-    val colorAccent = getColor(context, R.attr.colorAccent)
-    val colorAccentPrimary = getColor(context, R.attr.colorAccentPrimary)
-    val colorAccentSecondary = getColor(context, R.attr.colorAccentSecondary)
-    val colorAccentTertiary = getColor(context, R.attr.colorAccentTertiary)
-    val colorAccentPrimaryVariant = getColor(context, R.attr.colorAccentPrimaryVariant)
-    val colorAccentSecondaryVariant = getColor(context, R.attr.colorAccentSecondaryVariant)
-    val colorAccentTertiaryVariant = getColor(context, R.attr.colorAccentTertiaryVariant)
-    val colorSurface = getColor(context, R.attr.colorSurface)
-    val colorSurfaceHighlight = getColor(context, R.attr.colorSurfaceHighlight)
-    val colorSurfaceVariant = getColor(context, R.attr.colorSurfaceVariant)
-    val colorSurfaceHeader = getColor(context, R.attr.colorSurfaceHeader)
-    val colorError = getColor(context, R.attr.colorError)
-    val colorBackground = getColor(context, R.attr.colorBackground)
-    val colorBackgroundFloating = getColor(context, R.attr.colorBackgroundFloating)
-    val panelColorBackground = getColor(context, R.attr.panelColorBackground)
-    val textColorPrimary = getColor(context, R.attr.textColorPrimary)
-    val textColorSecondary = getColor(context, R.attr.textColorSecondary)
-    val textColorTertiary = getColor(context, R.attr.textColorTertiary)
-    val textColorPrimaryInverse = getColor(context, R.attr.textColorPrimaryInverse)
-    val textColorSecondaryInverse = getColor(context, R.attr.textColorSecondaryInverse)
-    val textColorTertiaryInverse = getColor(context, R.attr.textColorTertiaryInverse)
-    val textColorOnAccent = getColor(context, R.attr.textColorOnAccent)
-    val colorForeground = getColor(context, R.attr.colorForeground)
-    val colorForegroundInverse = getColor(context, R.attr.colorForegroundInverse)
+    val colorSurfaceBright = getColor(context, R.attr.materialColorSurfaceBright)
+    val colorSurfaceContainerHigh = getColor(context, R.attr.materialColorSurfaceContainerHigh)
 
     companion object {
         fun getColor(context: Context, attr: Int): Color {
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/Android.bp b/packages/SystemUI/accessibility/accessibilitymenu/Android.bp
index 140c10d..f358417 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/Android.bp
+++ b/packages/SystemUI/accessibility/accessibilitymenu/Android.bp
@@ -18,6 +18,15 @@
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
+// This filegroup is used by menu tests.
+filegroup {
+    name: "AccessibilityMenuSource",
+    srcs: [
+        "src/**/AccessibilityMenuService.java",
+        "src/**/A11yMenuShortcut.java",
+    ],
+}
+
 android_app {
     name: "AccessibilityMenu",
 
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_item.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_item.xml
index 39e5a8c..a902c5b 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_item.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_item.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/shortcutItem"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:paddingTop="@dimen/grid_item_padding"
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java
index c1f2aa8..8ca64d2 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.accessibility.accessibilitymenu;
 
+import android.Manifest;
 import android.accessibilityservice.AccessibilityButtonController;
 import android.accessibilityservice.AccessibilityService;
 import android.content.BroadcastReceiver;
@@ -51,8 +52,12 @@
 /** @hide */
 public class AccessibilityMenuService extends AccessibilityService
         implements View.OnTouchListener {
-    private static final String TAG = "A11yMenuService";
 
+    public static final String PACKAGE_NAME = AccessibilityMenuService.class.getPackageName();
+    public static final String INTENT_TOGGLE_MENU = ".toggle_menu";
+    public static final String INTENT_HIDE_MENU = ".hide_menu";
+
+    private static final String TAG = "A11yMenuService";
     private static final long BUFFER_MILLISECONDS_TO_PREVENT_UPDATE_FAILURE = 100L;
 
     private static final int BRIGHTNESS_UP_INCREMENT_GAMMA =
@@ -74,7 +79,8 @@
 
     // TODO(b/136716947): Support multi-display once a11y framework side is ready.
     private DisplayManager mDisplayManager;
-    final DisplayManager.DisplayListener mDisplayListener = new DisplayManager.DisplayListener() {
+    private final DisplayManager.DisplayListener mDisplayListener =
+            new DisplayManager.DisplayListener() {
         int mRotation;
 
         @Override
@@ -95,13 +101,20 @@
         }
     };
 
-    final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+    private final BroadcastReceiver mHideMenuReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             mA11yMenuLayout.hideMenu();
         }
     };
 
+    private final BroadcastReceiver mToggleMenuReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            mA11yMenuLayout.toggleVisibility();
+        }
+    };
+
     /**
      * Update a11y menu layout when large button setting is changed.
      */
@@ -172,7 +185,19 @@
     protected void onServiceConnected() {
         mA11yMenuLayout = new A11yMenuOverlayLayout(this);
 
-        registerReceiver(mBroadcastReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF));
+        IntentFilter hideMenuFilter = new IntentFilter();
+        hideMenuFilter.addAction(Intent.ACTION_SCREEN_OFF);
+        hideMenuFilter.addAction(PACKAGE_NAME + INTENT_HIDE_MENU);
+
+        // Including WRITE_SECURE_SETTINGS enforces that we only listen to apps
+        // with the restricted WRITE_SECURE_SETTINGS permission who broadcast this intent.
+        registerReceiver(mHideMenuReceiver, hideMenuFilter,
+                Manifest.permission.WRITE_SECURE_SETTINGS, null,
+                Context.RECEIVER_EXPORTED);
+        registerReceiver(mToggleMenuReceiver,
+                new IntentFilter(PACKAGE_NAME + INTENT_TOGGLE_MENU),
+                Manifest.permission.WRITE_SECURE_SETTINGS, null,
+                Context.RECEIVER_EXPORTED);
 
         mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
         mPrefs.registerOnSharedPreferenceChangeListener(mSharedPreferenceChangeListener);
@@ -260,7 +285,8 @@
      * @param increment The increment amount in gamma-space
      */
     private void adjustBrightness(int increment) {
-        BrightnessInfo info = getDisplay().getBrightnessInfo();
+        Display display = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
+        BrightnessInfo info = display.getBrightnessInfo();
         int gamma = BrightnessUtils.convertLinearToGammaFloat(
                 info.brightness,
                 info.brightnessMinimum,
@@ -275,7 +301,7 @@
                 info.brightnessMinimum,
                 info.brightnessMaximum
         );
-        mDisplayManager.setBrightness(getDisplayId(), brightness);
+        mDisplayManager.setBrightness(display.getDisplayId(), brightness);
         mA11yMenuLayout.showSnackbar(
                 getString(R.string.brightness_percentage_label,
                         (gamma / (BrightnessUtils.GAMMA_SPACE_MAX / 100))));
@@ -310,7 +336,8 @@
 
     @Override
     public boolean onUnbind(Intent intent) {
-        unregisterReceiver(mBroadcastReceiver);
+        unregisterReceiver(mHideMenuReceiver);
+        unregisterReceiver(mToggleMenuReceiver);
         mPrefs.unregisterOnSharedPreferenceChangeListener(mSharedPreferenceChangeListener);
         sInitialized = false;
         return super.onUnbind(intent);
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuAdapter.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuAdapter.java
index 6f0fe37..6ae65cb 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuAdapter.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuAdapter.java
@@ -21,6 +21,7 @@
 import android.view.TouchDelegate;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.BaseAdapter;
 import android.widget.ImageButton;
 import android.widget.TextView;
@@ -146,6 +147,15 @@
 
             shortcutIconButton.setBackground(
                     mShortcutDrawableUtils.createAdaptiveIconDrawable(shortcutItem.imageColor));
+
+            shortcutIconButton.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+                @Override
+                public void onInitializeAccessibilityNodeInfo(
+                        View host, AccessibilityNodeInfo info) {
+                    super.onInitializeAccessibilityNodeInfo(host, info);
+                    info.setUniqueId(host.getTag().toString());
+                }
+            });
         }
     }
 }
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/tests/Android.bp b/packages/SystemUI/accessibility/accessibilitymenu/tests/Android.bp
new file mode 100644
index 0000000..1757dda
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/tests/Android.bp
@@ -0,0 +1,43 @@
+//
+// 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+    name: "AccessibilityMenuServiceTests",
+    certificate: "platform",
+    libs: [
+        "android.test.runner",
+        "android.test.base",
+    ],
+    static_libs: [
+        "androidx.test.core",
+        "androidx.test.runner",
+        "androidx.test.ext.junit",
+        "compatibility-device-util-axt",
+        "platform-test-annotations",
+        "truth-prebuilt",
+    ],
+    srcs: [
+        "src/**/*.java",
+        ":AccessibilityMenuSource",
+    ],
+    platform_apis: true,
+    test_suites: ["device-tests"],
+    instrumentation_for: "AccessibilityMenu",
+}
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/tests/AndroidManifest.xml b/packages/SystemUI/accessibility/accessibilitymenu/tests/AndroidManifest.xml
new file mode 100644
index 0000000..7be6ca7
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/tests/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    package="com.android.systemui.accessibility.accessibilitymenu.tests">
+
+    <!-- Needed to write to Settings.Secure to enable and disable the service under test. -->
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+    <uses-permission android:name="android.permission.CONTROL_DISPLAY_BRIGHTNESS"/>
+
+    <application android:debuggable="true">
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.systemui.accessibility.accessibilitymenu.tests"
+        android:label="AccessibilityMenu Test Cases">
+    </instrumentation>
+</manifest>
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/tests/AndroidTest.xml b/packages/SystemUI/accessibility/accessibilitymenu/tests/AndroidTest.xml
new file mode 100644
index 0000000..39bee53
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/tests/AndroidTest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Runs AccessibilityMenu Test Cases.">
+    <option name="test-tag" value="AccessibilityMenuServiceTests" />
+
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="AccessibilityMenuServiceTests.apk" />
+        <option name="aapt-version" value="AAPT2" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.systemui.accessibility.accessibilitymenu.tests" />
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+    </test>
+</configuration>
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/tests/TEST_MAPPING b/packages/SystemUI/accessibility/accessibilitymenu/tests/TEST_MAPPING
new file mode 100644
index 0000000..2bd52b5
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/tests/TEST_MAPPING
@@ -0,0 +1,15 @@
+{
+  "presubmit": [
+    {
+      "name": "AccessibilityMenuServiceTests",
+      "options": [
+        {
+          "include-annotation": "android.platform.test.annotations.Presubmit"
+        },
+        {
+          "exclude-annotation": "android.support.test.filters.FlakyTest"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java b/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java
new file mode 100644
index 0000000..529a70c
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java
@@ -0,0 +1,183 @@
+/*
+ * 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.accessibility.accessibilitymenu.tests;
+
+import static com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService.INTENT_HIDE_MENU;
+import static com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService.INTENT_TOGGLE_MENU;
+import static com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService.PACKAGE_NAME;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.app.Instrumentation;
+import android.app.UiAutomation;
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.display.BrightnessInfo;
+import android.hardware.display.DisplayManager;
+import android.provider.Settings;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.TestUtils;
+import com.android.systemui.accessibility.accessibilitymenu.model.A11yMenuShortcut.ShortcutId;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityMenuServiceTest {
+    private static final String TAG = "A11yMenuServiceTest";
+
+    private static final int TIMEOUT_SERVICE_STATUS_CHANGE_S = 5;
+    private static final int TIMEOUT_UI_CHANGE_S = 5;
+
+    private static Instrumentation sInstrumentation;
+    private static UiAutomation sUiAutomation;
+
+    private static AccessibilityManager sAccessibilityManager;
+
+    @BeforeClass
+    public static void classSetup() throws Throwable {
+        final String serviceName = PACKAGE_NAME + "/.AccessibilityMenuService";
+        sInstrumentation = InstrumentationRegistry.getInstrumentation();
+        sUiAutomation = sInstrumentation.getUiAutomation(
+                UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
+        final Context context = sInstrumentation.getContext();
+        sAccessibilityManager = context.getSystemService(AccessibilityManager.class);
+
+        // Disable all a11yServices if any are active.
+        if (!sAccessibilityManager.getEnabledAccessibilityServiceList(
+                AccessibilityServiceInfo.FEEDBACK_ALL_MASK).isEmpty()) {
+            Settings.Secure.putString(context.getContentResolver(),
+                    Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, "");
+            TestUtils.waitUntil("Failed to disable all services",
+                    TIMEOUT_SERVICE_STATUS_CHANGE_S,
+                    () -> sAccessibilityManager.getEnabledAccessibilityServiceList(
+                            AccessibilityServiceInfo.FEEDBACK_ALL_MASK).isEmpty());
+        }
+
+        // Enable a11yMenu service.
+        Settings.Secure.putString(context.getContentResolver(),
+                Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, serviceName);
+
+        TestUtils.waitUntil("Failed to enable service",
+                TIMEOUT_SERVICE_STATUS_CHANGE_S,
+                () -> sAccessibilityManager.getEnabledAccessibilityServiceList(
+                        AccessibilityServiceInfo.FEEDBACK_ALL_MASK).stream().filter(
+                                info -> info.getId().contains(serviceName)).count() == 1);
+    }
+
+    @AfterClass
+    public static void classTeardown() throws Throwable {
+        Settings.Secure.putString(sInstrumentation.getTargetContext().getContentResolver(),
+                Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, "");
+    }
+
+    private boolean isMenuVisible() {
+        return sUiAutomation.getRootInActiveWindow() != null
+                && sUiAutomation.getRootInActiveWindow().getPackageName().toString().equals(
+                PACKAGE_NAME);
+    }
+
+    private void openMenu() throws Throwable {
+        if (isMenuVisible()) {
+            return;
+        }
+        Intent intent = new Intent(PACKAGE_NAME + INTENT_TOGGLE_MENU);
+        sInstrumentation.getContext().sendBroadcast(intent);
+        TestUtils.waitUntil("Timed out before menu could appear.",
+                TIMEOUT_UI_CHANGE_S, () -> isMenuVisible());
+    }
+
+    private void closeMenu() throws Throwable {
+        if (!isMenuVisible()) {
+            return;
+        }
+        Intent intent = new Intent(PACKAGE_NAME + INTENT_HIDE_MENU);
+        sInstrumentation.getContext().sendBroadcast(intent);
+        TestUtils.waitUntil("Timed out before menu could close.",
+                TIMEOUT_UI_CHANGE_S, () -> !isMenuVisible());
+    }
+
+    private List<AccessibilityNodeInfo> getGridButtonList() {
+        return sUiAutomation.getRootInActiveWindow()
+                        .findAccessibilityNodeInfosByViewId(PACKAGE_NAME + ":id/shortcutIconBtn");
+    }
+
+    private AccessibilityNodeInfo findGridButtonInfo(
+            List<AccessibilityNodeInfo> buttons, String text) {
+        for (AccessibilityNodeInfo button: buttons) {
+            if (button.getUniqueId().equals(text)) {
+                return button;
+            }
+        }
+        return null;
+    }
+
+    @Test
+    public void testAdjustBrightness() throws Throwable {
+        openMenu();
+
+        Context context = sInstrumentation.getTargetContext();
+        DisplayManager displayManager = context.getSystemService(
+                DisplayManager.class);
+        float resetBrightness = displayManager.getBrightness(context.getDisplayId());
+
+        List<AccessibilityNodeInfo> buttons = getGridButtonList();
+        AccessibilityNodeInfo brightnessUpButton = findGridButtonInfo(buttons,
+                String.valueOf(ShortcutId.ID_BRIGHTNESS_UP_VALUE.ordinal()));
+        AccessibilityNodeInfo brightnessDownButton = findGridButtonInfo(buttons,
+                String.valueOf(ShortcutId.ID_BRIGHTNESS_DOWN_VALUE.ordinal()));
+
+        int clickId = AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK.getId();
+        BrightnessInfo brightnessInfo = displayManager.getDisplay(
+                context.getDisplayId()).getBrightnessInfo();
+
+        try {
+            displayManager.setBrightness(context.getDisplayId(), brightnessInfo.brightnessMinimum);
+            TestUtils.waitUntil("Could not change to minimum brightness",
+                    TIMEOUT_UI_CHANGE_S,
+                    () -> displayManager.getBrightness(context.getDisplayId())
+                            == brightnessInfo.brightnessMinimum);
+            brightnessUpButton.performAction(clickId);
+            TestUtils.waitUntil("Did not detect an increase in brightness.",
+                    TIMEOUT_UI_CHANGE_S,
+                    () -> displayManager.getBrightness(context.getDisplayId())
+                            > brightnessInfo.brightnessMinimum);
+
+            displayManager.setBrightness(context.getDisplayId(), brightnessInfo.brightnessMaximum);
+            TestUtils.waitUntil("Could not change to maximum brightness",
+                    TIMEOUT_UI_CHANGE_S,
+                    () -> displayManager.getBrightness(context.getDisplayId())
+                            == brightnessInfo.brightnessMaximum);
+            brightnessDownButton.performAction(clickId);
+            TestUtils.waitUntil("Did not detect a decrease in brightness.",
+                    TIMEOUT_UI_CHANGE_S,
+                    () -> displayManager.getBrightness(context.getDisplayId())
+                            < brightnessInfo.brightnessMaximum);
+        } finally {
+            displayManager.setBrightness(context.getDisplayId(), resetBrightness);
+            closeMenu();
+        }
+    }
+}
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/DemotingTestWithoutBugDetector.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/DemotingTestWithoutBugDetector.kt
new file mode 100644
index 0000000..f64ea45
--- /dev/null
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/DemotingTestWithoutBugDetector.kt
@@ -0,0 +1,77 @@
+/*
+ * 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.internal.systemui.lint
+
+import com.android.tools.lint.client.api.UElementHandler
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import org.jetbrains.uast.UAnnotation
+import org.jetbrains.uast.UElement
+
+class DemotingTestWithoutBugDetector : Detector(), SourceCodeScanner {
+    override fun getApplicableUastTypes(): List<Class<out UElement>> {
+        return listOf(UAnnotation::class.java)
+    }
+
+    override fun createUastHandler(context: JavaContext): UElementHandler {
+        return object : UElementHandler() {
+            override fun visitAnnotation(node: UAnnotation) {
+                if (node.qualifiedName !in DEMOTING_ANNOTATION) {
+                    return
+                }
+                val bugId = node.findAttributeValue("bugId")!!.evaluate() as Int
+                if (bugId <= 0) {
+                    val location = context.getLocation(node)
+                    val message = "Please attach a bug id to track demoted test"
+                    context.report(ISSUE, node, location, message)
+                }
+            }
+        }
+    }
+
+    companion object {
+        val DEMOTING_ANNOTATION =
+            listOf("androidx.test.filters.FlakyTest", "android.platform.test.annotations.FlakyTest")
+
+        @JvmField
+        val ISSUE: Issue =
+            Issue.create(
+                id = "DemotingTestWithoutBug",
+                briefDescription = "Demoting a test without attaching a bug.",
+                explanation =
+                    """
+                    Annotations (`@FlakyTest`) demote tests to an unmonitored \
+                    test suite. Please set the `bugId` field in such annotations to track \
+                    the test status.
+                    """,
+                category = Category.TESTING,
+                priority = 8,
+                severity = Severity.WARNING,
+                implementation =
+                    Implementation(
+                        DemotingTestWithoutBugDetector::class.java,
+                        Scope.JAVA_FILE_SCOPE
+                    )
+            )
+    }
+}
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
index 84f7050..387b67d 100644
--- a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
@@ -39,7 +39,8 @@
                 RegisterReceiverViaContextDetector.ISSUE,
                 SoftwareBitmapDetector.ISSUE,
                 NonInjectedServiceDetector.ISSUE,
-                StaticSettingsProviderDetector.ISSUE
+                StaticSettingsProviderDetector.ISSUE,
+                DemotingTestWithoutBugDetector.ISSUE
             )
 
     override val api: Int
diff --git a/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/DemotingTestWithoutBugDetectorTest.kt b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/DemotingTestWithoutBugDetectorTest.kt
new file mode 100644
index 0000000..557c300
--- /dev/null
+++ b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/DemotingTestWithoutBugDetectorTest.kt
@@ -0,0 +1,151 @@
+/*
+ * 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.internal.systemui.lint
+
+import com.android.tools.lint.checks.infrastructure.TestFile
+import com.android.tools.lint.checks.infrastructure.TestFiles
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+import org.junit.Test
+
+class DemotingTestWithoutBugDetectorTest : SystemUILintDetectorTest() {
+
+    override fun getDetector(): Detector = DemotingTestWithoutBugDetector()
+    override fun getIssues(): List<Issue> = listOf(DemotingTestWithoutBugDetector.ISSUE)
+
+    @Test
+    fun testMarkFlaky_withBugId() {
+        lint()
+            .files(
+                TestFiles.java(
+                        """
+                        package test.pkg;
+                        import androidx.test.filters.FlakyTest;
+
+                        @FlakyTest(bugId = 123)
+                        public class TestClass {
+                            public void testCase() {}
+                        }
+                    """
+                    )
+                    .indented(),
+                *stubs
+            )
+            .issues(DemotingTestWithoutBugDetector.ISSUE)
+            .run()
+            .expectClean()
+
+        lint()
+            .files(
+                TestFiles.java(
+                        """
+                        package test.pkg;
+                        import android.platform.test.annotations.FlakyTest;
+
+                        @FlakyTest(bugId = 123)
+                        public class TestClass {
+                            public void testCase() {}
+                        }
+                    """
+                    )
+                    .indented(),
+                *stubs
+            )
+            .issues(DemotingTestWithoutBugDetector.ISSUE)
+            .run()
+            .expectClean()
+    }
+
+    @Test
+    fun testMarkFlaky_withoutBugId() {
+        lint()
+            .files(
+                TestFiles.java(
+                        """
+                        package test.pkg;
+                        import androidx.test.filters.FlakyTest;
+
+                        @FlakyTest
+                        public class TestClass {
+                            public void testCase() {}
+                        }
+                    """
+                    )
+                    .indented(),
+                *stubs
+            )
+            .issues(DemotingTestWithoutBugDetector.ISSUE)
+            .run()
+            .expect(
+                """
+                src/test/pkg/TestClass.java:4: Warning: Please attach a bug id to track demoted test [DemotingTestWithoutBug]
+                @FlakyTest
+                ~~~~~~~~~~
+                0 errors, 1 warnings
+                """
+            )
+
+        lint()
+            .files(
+                TestFiles.java(
+                        """
+                        package test.pkg;
+                        import android.platform.test.annotations.FlakyTest;
+
+                        @FlakyTest
+                        public class TestClass {
+                            public void testCase() {}
+                        }
+                    """
+                    )
+                    .indented(),
+                *stubs
+            )
+            .issues(DemotingTestWithoutBugDetector.ISSUE)
+            .run()
+            .expect(
+                """
+                src/test/pkg/TestClass.java:4: Warning: Please attach a bug id to track demoted test [DemotingTestWithoutBug]
+                @FlakyTest
+                ~~~~~~~~~~
+                0 errors, 1 warnings
+                """
+            )
+    }
+
+    private val filtersFlakyTestStub: TestFile =
+        java(
+            """
+        package androidx.test.filters;
+
+        public @interface FlakyTest {
+            int bugId() default -1;
+        }
+        """
+        )
+    private val annotationsFlakyTestStub: TestFile =
+        java(
+            """
+        package android.platform.test.annotations;
+
+        public @interface FlakyTest {
+            int bugId() default -1;
+        }
+        """
+        )
+    private val stubs = arrayOf(filtersFlakyTestStub, annotationsFlakyTestStub)
+}
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index b6a78f5..caf3233 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -82,7 +82,7 @@
     <!-- The vertical margin between the date and the owner info. -->
 
     <!-- The translation for disappearing security views after having solved them. -->
-    <dimen name="disappear_y_translation">-32dp</dimen>
+    <dimen name="disappear_y_translation">-50dp</dimen>
 
     <!-- Dimens for animation for the Bouncer PIN view -->
     <dimen name="pin_view_trans_y_entry">120dp</dimen>
diff --git a/packages/SystemUI/res/layout/window_magnification_settings_view.xml b/packages/SystemUI/res/layout/window_magnification_settings_view.xml
index 7dfe7c4..ae0f8f4 100644
--- a/packages/SystemUI/res/layout/window_magnification_settings_view.xml
+++ b/packages/SystemUI/res/layout/window_magnification_settings_view.xml
@@ -21,7 +21,9 @@
     android:layout_height="wrap_content"
     android:background="@drawable/accessibility_magnification_setting_view_bg"
     android:orientation="vertical"
-    android:padding="@dimen/magnification_setting_background_padding">
+    android:padding="@dimen/magnification_setting_background_padding"
+    android:focusable="true"
+    android:contentDescription="@string/accessibility_magnification_settings_panel_description">
     <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 4d989a6..f4b3b87 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2337,6 +2337,8 @@
     <string name="magnification_mode_switch_state_window">Magnify part of screen</string>
     <!-- Click action label for magnification switch. [CHAR LIMIT=NONE] -->
     <string name="magnification_mode_switch_click_label">Switch</string>
+    <!-- Click action label for magnification settings panel. [CHAR LIMIT=NONE] -->
+    <string name="magnification_open_settings_click_label">Open magnification settings</string>
     <!-- Label of the corner of a rectangle that you can tap and drag to resize the magnification area. [CHAR LIMIT=NONE] -->
     <string name="magnification_drag_corner_to_resize">Drag corner to resize</string>
 
@@ -2358,6 +2360,8 @@
     <!-- Description of the window magnification Bottom handle [CHAR LIMIT=NONE]-->
     <string name="accessibility_magnification_bottom_handle">Bottom handle</string>
 
+    <!-- Description of the window magnification panel [CHAR LIMIT=NONE]-->
+    <string name="accessibility_magnification_settings_panel_description">Magnification settings</string>
     <!-- Title of the window magnification panel option Magnifier size [CHAR LIMIT=NONE]-->
     <string name="accessibility_magnifier_size">Magnifier size</string>
     <!-- Title of the window magnification panel option Zoom [CHAR LIMIT=NONE]-->
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt
index fe8b8c9..c98e9b4 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt
@@ -40,7 +40,7 @@
     var keyguardGoingAway: Boolean = false,
     var listeningForFaceAssistant: Boolean = false,
     var occludingAppRequestingFaceAuth: Boolean = false,
-    val postureAllowsListening: Boolean = false,
+    var postureAllowsListening: Boolean = false,
     var primaryUser: Boolean = false,
     var secureCameraLaunched: Boolean = false,
     var supportsDetect: Boolean = false,
@@ -70,6 +70,7 @@
             listeningForFaceAssistant.toString(),
             occludingAppRequestingFaceAuth.toString(),
             primaryUser.toString(),
+            postureAllowsListening.toString(),
             secureCameraLaunched.toString(),
             supportsDetect.toString(),
             switchingUser.toString(),
@@ -109,6 +110,7 @@
                 listeningForFaceAssistant = model.listeningForFaceAssistant
                 occludingAppRequestingFaceAuth = model.occludingAppRequestingFaceAuth
                 primaryUser = model.primaryUser
+                postureAllowsListening = model.postureAllowsListening
                 secureCameraLaunched = model.secureCameraLaunched
                 supportsDetect = model.supportsDetect
                 switchingUser = model.switchingUser
@@ -152,6 +154,7 @@
                 "listeningForFaceAssistant",
                 "occludingAppRequestingFaceAuth",
                 "primaryUser",
+                "postureAllowsListening",
                 "secureCameraLaunched",
                 "supportsDetect",
                 "switchingUser",
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 2949616..66d5d09 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -39,7 +39,6 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.app.Activity;
@@ -1068,13 +1067,10 @@
 
             int yTranslation = mResources.getDimensionPixelSize(R.dimen.disappear_y_translation);
 
-            AnimatorSet anims = new AnimatorSet();
             ObjectAnimator yAnim = ObjectAnimator.ofFloat(mView, View.TRANSLATION_Y, yTranslation);
-            ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(mView, View.ALPHA, 0f);
-
-            anims.setInterpolator(Interpolators.STANDARD_ACCELERATE);
-            anims.playTogether(alphaAnim, yAnim);
-            anims.start();
+            yAnim.setInterpolator(Interpolators.STANDARD_ACCELERATE);
+            yAnim.setDuration(500);
+            yAnim.start();
         }
 
         private void setupUserSwitcher() {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index f1abdc6..06258b2 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -637,12 +637,17 @@
 
     public void startAppearAnimation() {
         if (mCurrentSecurityMode != SecurityMode.None) {
-            mView.setAlpha(1f);
+            setAlpha(1f);
             mView.startAppearAnimation(mCurrentSecurityMode);
             getCurrentSecurityController().startAppearAnimation();
         }
     }
 
+    /** Set the alpha of the security container view */
+    public void setAlpha(float alpha) {
+        mView.setAlpha(alpha);
+    }
+
     public boolean startDisappearAnimation(Runnable onFinishRunnable) {
         boolean didRunAnimation = false;
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 866b502..21d35c9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -160,6 +160,7 @@
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.policy.DevicePostureController;
+import com.android.systemui.statusbar.policy.DevicePostureController.DevicePostureInt;
 import com.android.systemui.telephony.TelephonyListenerManager;
 import com.android.systemui.util.Assert;
 import com.android.systemui.util.settings.SecureSettings;
@@ -368,7 +369,7 @@
     private final FaceManager mFaceManager;
     private final LockPatternUtils mLockPatternUtils;
     @VisibleForTesting
-    @DevicePostureController.DevicePostureInt
+    @DevicePostureInt
     protected int mConfigFaceAuthSupportedPosture;
 
     private KeyguardBypassController mKeyguardBypassController;
@@ -1862,10 +1863,15 @@
     final DevicePostureController.Callback mPostureCallback =
             new DevicePostureController.Callback() {
                 @Override
-                public void onPostureChanged(int posture) {
+                public void onPostureChanged(@DevicePostureInt int posture) {
+                    boolean currentPostureAllowsFaceAuth = doesPostureAllowFaceAuth(mPostureState);
+                    boolean newPostureAllowsFaceAuth = doesPostureAllowFaceAuth(posture);
                     mPostureState = posture;
-                    updateFaceListeningState(BIOMETRIC_ACTION_UPDATE,
-                            FACE_AUTH_UPDATED_POSTURE_CHANGED);
+                    if (currentPostureAllowsFaceAuth && !newPostureAllowsFaceAuth) {
+                        mLogger.d("New posture does not allow face auth, stopping it");
+                        updateFaceListeningState(BIOMETRIC_ACTION_STOP,
+                                FACE_AUTH_UPDATED_POSTURE_CHANGED);
+                    }
                 }
             };
 
@@ -2901,9 +2907,7 @@
         final boolean biometricEnabledForUser = mBiometricEnabledForUser.get(user);
         final boolean shouldListenForFaceAssistant = shouldListenForFaceAssistant();
         final boolean isUdfpsFingerDown = mAuthController.isUdfpsFingerDown();
-        final boolean isPostureAllowedForFaceAuth =
-                mConfigFaceAuthSupportedPosture == 0 /* DEVICE_POSTURE_UNKNOWN */ ? true
-                        : (mPostureState == mConfigFaceAuthSupportedPosture);
+        final boolean isPostureAllowedForFaceAuth = doesPostureAllowFaceAuth(mPostureState);
         // Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an
         // instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware.
         final boolean shouldListen =
@@ -2952,6 +2956,11 @@
         return shouldListen;
     }
 
+    private boolean doesPostureAllowFaceAuth(@DevicePostureInt int posture) {
+        return mConfigFaceAuthSupportedPosture == DEVICE_POSTURE_UNKNOWN
+                || (posture == mConfigFaceAuthSupportedPosture);
+    }
+
     private void logListenerModelData(@NonNull KeyguardListenModel model) {
         mLogger.logKeyguardListenerModel(model);
         if (model instanceof KeyguardFingerprintListenModel) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index b111e1f..d35c77c 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -1468,7 +1468,7 @@
             super.onInitializeAccessibilityNodeInfo(host, info);
             final AccessibilityAction clickAction = new AccessibilityAction(
                     AccessibilityAction.ACTION_CLICK.getId(), mContext.getResources().getString(
-                    R.string.magnification_mode_switch_click_label));
+                    R.string.magnification_open_settings_click_label));
             info.addAction(clickAction);
             info.setClickable(true);
             info.addAction(
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
index 15264e64..e1f3c6c 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
@@ -215,9 +215,7 @@
 
         private boolean performA11yAction(View view, int action) {
             final Rect windowBounds = mWindowManager.getCurrentWindowMetrics().getBounds();
-            if (action == AccessibilityAction.ACTION_CLICK.getId()) {
-                handleSingleTap(view);
-            } else if (action == R.id.accessibility_action_move_up) {
+            if (action == R.id.accessibility_action_move_up) {
                 moveButton(0, -windowBounds.height());
             } else if (action == R.id.accessibility_action_move_down) {
                 moveButton(0, windowBounds.height());
@@ -264,8 +262,6 @@
             } else if (id == R.id.magnifier_full_button) {
                 hideSettingPanel();
                 toggleMagnificationMode();
-            } else {
-                hideSettingPanel();
             }
         }
     };
@@ -273,7 +269,6 @@
     @Override
     public boolean onSingleTap(View view) {
         mSingleTapDetected = true;
-        handleSingleTap(view);
         return true;
     }
 
@@ -358,6 +353,10 @@
             }
 
             mWindowManager.addView(mSettingView, mParams);
+            if (resetPosition) {
+                // Request focus on the settings panel when position of the panel is reset.
+                mSettingView.requestFocus();
+            }
 
             // Exclude magnification switch button from system gesture area.
             setSystemGestureExclusion();
@@ -385,8 +384,8 @@
         mSettingView = (LinearLayout) View.inflate(mContext,
                 R.layout.window_magnification_settings_view, null);
 
-        mSettingView.setClickable(true);
         mSettingView.setFocusable(true);
+        mSettingView.setFocusableInTouchMode(true);
         mSettingView.setOnTouchListener(this::onTouch);
 
         mPanelView = mSettingView.findViewById(R.id.magnifier_panel_view);
@@ -499,22 +498,6 @@
         }
     }
 
-    private void handleSingleTap(View view) {
-        int id = view.getId();
-        if (id == R.id.magnifier_small_button) {
-            setMagnifierSize(MagnificationSize.SMALL);
-        } else if (id == R.id.magnifier_medium_button) {
-            setMagnifierSize(MagnificationSize.MEDIUM);
-        } else if (id == R.id.magnifier_large_button) {
-            setMagnifierSize(MagnificationSize.LARGE);
-        } else if (id == R.id.magnifier_full_button) {
-            hideSettingPanel();
-            toggleMagnificationMode();
-        } else {
-            hideSettingPanel();
-        }
-    }
-
     public void editMagnifierSizeMode(boolean enable) {
         setEditMagnifierSizeMode(enable);
         updateSelectedButton(MagnificationSize.NONE);
@@ -551,7 +534,7 @@
                 LayoutParams.WRAP_CONTENT,
                 LayoutParams.WRAP_CONTENT,
                 LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY,
-                LayoutParams.FLAG_NOT_FOCUSABLE,
+                /* _flags= */ 0,
                 PixelFormat.TRANSPARENT);
         params.gravity = Gravity.TOP | Gravity.START;
         params.accessibilityTitle = getAccessibilityWindowTitle(context);
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
index a5beb4e..3cf26b3 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
@@ -23,15 +23,16 @@
 import com.android.systemui.keyguard.WorkLockActivity;
 import com.android.systemui.people.PeopleSpaceActivity;
 import com.android.systemui.people.widget.LaunchConversationActivity;
-import com.android.systemui.screenshot.AppClipsActivity;
-import com.android.systemui.screenshot.AppClipsTrampolineActivity;
 import com.android.systemui.screenshot.LongScreenshotActivity;
+import com.android.systemui.screenshot.appclips.AppClipsActivity;
+import com.android.systemui.screenshot.appclips.AppClipsTrampolineActivity;
 import com.android.systemui.sensorprivacy.SensorUseStartedActivity;
 import com.android.systemui.sensorprivacy.television.TvSensorPrivacyChangedActivity;
 import com.android.systemui.sensorprivacy.television.TvUnblockSensorActivity;
 import com.android.systemui.settings.brightness.BrightnessDialog;
 import com.android.systemui.statusbar.tv.notifications.TvNotificationPanelActivity;
 import com.android.systemui.tuner.TunerActivity;
+import com.android.systemui.usb.UsbAccessoryUriActivity;
 import com.android.systemui.usb.UsbConfirmActivity;
 import com.android.systemui.usb.UsbDebuggingActivity;
 import com.android.systemui.usb.UsbDebuggingSecondaryUserActivity;
@@ -97,6 +98,12 @@
     @ClassKey(UsbConfirmActivity.class)
     public abstract Activity bindUsbConfirmActivity(UsbConfirmActivity activity);
 
+    /** Inject into UsbAccessoryUriActivity. */
+    @Binds
+    @IntoMap
+    @ClassKey(UsbAccessoryUriActivity.class)
+    public abstract Activity bindUsbAccessoryUriActivity(UsbAccessoryUriActivity activity);
+
     /** Inject into CreateUserActivity. */
     @Binds
     @IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
index 73c2289..a7b3bbc 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
@@ -254,7 +254,10 @@
         mCurrentScrimController = mScrimManager.getCurrentController();
 
         session.registerCallback(() -> {
-            mVelocityTracker.recycle();
+            if (mVelocityTracker != null) {
+                mVelocityTracker.recycle();
+                mVelocityTracker = null;
+            }
             mScrimManager.removeCallback(mScrimManagerCallback);
             mCapture = null;
             mNotificationShadeWindowController.setForcePluginOpen(false, this);
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 3608b91..5817415 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -461,7 +461,7 @@
     @Keep
     @JvmField
     val ENABLE_PIP_SIZE_LARGE_SCREEN =
-        sysPropBooleanFlag(1114, "persist.wm.debug.enable_pip_size_large_screen", default = false)
+        sysPropBooleanFlag(1114, "persist.wm.debug.enable_pip_size_large_screen", default = true)
 
     // TODO(b/265998256): Tracking bug
     @Keep
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 2ad1ab7..288f0cc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -966,13 +966,24 @@
                 public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
                         RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
                         IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException {
+                    if (!handleOnAnimationStart(
+                                transit, apps, wallpapers, nonApps, finishedCallback)) {
+                        // Usually we rely on animation completion to synchronize occluded status,
+                        // but there was no animation to play, so just update it now.
+                        setOccluded(true /* isOccluded */, false /* animate */);
+                    }
+                }
+
+                private boolean handleOnAnimationStart(int transit, RemoteAnimationTarget[] apps,
+                        RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
+                        IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException {
                     if (apps == null || apps.length == 0 || apps[0] == null) {
                         if (DEBUG) {
                             Log.d(TAG, "No apps provided to the OccludeByDream runner; "
                                     + "skipping occluding animation.");
                         }
                         finishedCallback.onAnimationFinished();
-                        return;
+                        return false;
                     }
 
                     final RemoteAnimationTarget primary = apps[0];
@@ -982,7 +993,7 @@
                         Log.w(TAG, "The occluding app isn't Dream; "
                                 + "finishing up. Please check that the config is correct.");
                         finishedCallback.onAnimationFinished();
-                        return;
+                        return false;
                     }
 
                     final SyncRtSurfaceTransactionApplier applier =
@@ -1031,6 +1042,7 @@
 
                         mOccludeByDreamAnimator.start();
                     });
+                    return true;
                 }
             };
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index 47ef0fa..cb89106 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -46,6 +46,8 @@
 import com.android.systemui.keyguard.data.repository.KeyguardRepositoryModule;
 import com.android.systemui.keyguard.domain.interactor.StartKeyguardTransitionModule;
 import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceModule;
+import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger;
+import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLoggerImpl;
 import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shade.ShadeController;
@@ -64,6 +66,8 @@
 import dagger.Lazy;
 import dagger.Module;
 import dagger.Provides;
+import kotlinx.coroutines.CoroutineDispatcher;
+import kotlinx.coroutines.CoroutineScope;
 
 /**
  * Dagger Module providing keyguard.
@@ -153,4 +157,10 @@
     public ViewMediatorCallback providesViewMediatorCallback(KeyguardViewMediator viewMediator) {
         return viewMediator.getViewMediatorCallback();
     }
+
+    /** */
+    @Provides
+    public KeyguardQuickAffordancesMetricsLogger providesKeyguardQuickAffordancesMetricsLogger() {
+        return new KeyguardQuickAffordancesMetricsLoggerImpl();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index a3b3d0f..76f20d25 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -80,6 +80,9 @@
      */
     val isKeyguardShowing: Flow<Boolean>
 
+    /** Is the keyguard in a unlocked state? */
+    val isKeyguardUnlocked: Flow<Boolean>
+
     /** Is an activity showing over the keyguard? */
     val isKeyguardOccluded: Flow<Boolean>
 
@@ -278,6 +281,31 @@
             }
             .distinctUntilChanged()
 
+    override val isKeyguardUnlocked: Flow<Boolean> =
+        conflatedCallbackFlow {
+                val callback =
+                    object : KeyguardStateController.Callback {
+                        override fun onUnlockedChanged() {
+                            trySendWithFailureLogging(
+                                keyguardStateController.isUnlocked,
+                                TAG,
+                                "updated isKeyguardUnlocked"
+                            )
+                        }
+                    }
+
+                keyguardStateController.addCallback(callback)
+                // Adding the callback does not send an initial update.
+                trySendWithFailureLogging(
+                    keyguardStateController.isUnlocked,
+                    TAG,
+                    "initial isKeyguardUnlocked"
+                )
+
+                awaitClose { keyguardStateController.removeCallback(callback) }
+            }
+            .distinctUntilChanged()
+
     override val isKeyguardGoingAway: Flow<Boolean> = conflatedCallbackFlow {
         val callback =
             object : KeyguardStateController.Callback {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
index 0c4bca6..100bc59 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
@@ -68,8 +68,11 @@
     /**
      * Begin a transition from one state to another. Transitions are interruptible, and will issue a
      * [TransitionStep] with state = [TransitionState.CANCELED] before beginning the next one.
+     *
+     * When canceled, there are two options: to continue from the current position of the prior
+     * transition, or to reset the position. When [resetIfCanceled] == true, it will do the latter.
      */
-    fun startTransition(info: TransitionInfo): UUID?
+    fun startTransition(info: TransitionInfo, resetIfCanceled: Boolean = false): UUID?
 
     /**
      * Allows manual control of a transition. When calling [startTransition], the consumer must pass
@@ -130,7 +133,10 @@
         )
     }
 
-    override fun startTransition(info: TransitionInfo): UUID? {
+    override fun startTransition(
+        info: TransitionInfo,
+        resetIfCanceled: Boolean,
+    ): UUID? {
         if (lastStep.from == info.from && lastStep.to == info.to) {
             Log.i(TAG, "Duplicate call to start the transition, rejecting: $info")
             return null
@@ -138,7 +144,11 @@
         val startingValue =
             if (lastStep.transitionState != TransitionState.FINISHED) {
                 Log.i(TAG, "Transition still active: $lastStep, canceling")
-                lastStep.value
+                if (resetIfCanceled) {
+                    0f
+                } else {
+                    lastStep.value
+                }
             } else {
                 0f
             }
@@ -227,10 +237,7 @@
     }
 
     private fun trace(step: TransitionStep, isManual: Boolean) {
-        if (
-            step.transitionState != TransitionState.STARTED &&
-                step.transitionState != TransitionState.FINISHED
-        ) {
+        if (step.transitionState == TransitionState.RUNNING) {
             return
         }
         val traceName =
@@ -243,7 +250,10 @@
         val traceCookie = traceName.hashCode()
         if (step.transitionState == TransitionState.STARTED) {
             Trace.beginAsyncSection(traceName, traceCookie)
-        } else if (step.transitionState == TransitionState.FINISHED) {
+        } else if (
+            step.transitionState == TransitionState.FINISHED ||
+                step.transitionState == TransitionState.CANCELED
+        ) {
             Trace.endAsyncSection(traceName, traceCookie)
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
index 8715d1f..3beac0b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
@@ -34,7 +34,6 @@
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.launch
 
@@ -57,14 +56,7 @@
 
     private fun listenForDreamingToLockscreen() {
         scope.launch {
-            // Dependending on the dream, either dream state or occluded change will change first,
-            // so listen for both
-            combine(keyguardInteractor.isAbleToDream, keyguardInteractor.isKeyguardOccluded) {
-                    isAbleToDream,
-                    isKeyguardOccluded ->
-                    isAbleToDream && isKeyguardOccluded
-                }
-                .distinctUntilChanged()
+            keyguardInteractor.isAbleToDream
                 .sample(
                     combine(
                         keyguardInteractor.dozeTransitionModel,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index d01f489..911861d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -130,55 +130,59 @@
             shadeRepository.shadeModel
                 .sample(
                     combine(
-                        keyguardTransitionInteractor.finishedKeyguardState,
+                        keyguardTransitionInteractor.startedKeyguardTransitionStep,
                         keyguardInteractor.statusBarState,
-                        ::Pair
+                        keyguardInteractor.isKeyguardUnlocked,
+                        ::toTriple
                     ),
-                    ::toTriple
+                    ::toQuad
                 )
-                .collect { (shadeModel, keyguardState, statusBarState) ->
+                .collect { (shadeModel, keyguardState, statusBarState, isKeyguardUnlocked) ->
                     val id = transitionId
                     if (id != null) {
-                        // An existing `id` means a transition is started, and calls to
-                        // `updateTransition` will control it until FINISHED or CANCELED
-                        var nextState =
-                            if (shadeModel.expansionAmount == 0f) {
-                                TransitionState.FINISHED
-                            } else if (shadeModel.expansionAmount == 1f) {
-                                TransitionState.CANCELED
-                            } else {
-                                TransitionState.RUNNING
-                            }
-                        keyguardTransitionRepository.updateTransition(
-                            id,
-                            1f - shadeModel.expansionAmount,
-                            nextState,
-                        )
-
-                        if (
-                            nextState == TransitionState.CANCELED ||
-                                nextState == TransitionState.FINISHED
-                        ) {
-                            transitionId = null
-                        }
-
-                        // If canceled, just put the state back
-                        if (nextState == TransitionState.CANCELED) {
-                            keyguardTransitionRepository.startTransition(
-                                TransitionInfo(
-                                    ownerName = name,
-                                    from = KeyguardState.PRIMARY_BOUNCER,
-                                    to = KeyguardState.LOCKSCREEN,
-                                    animator = getAnimator(0.milliseconds)
-                                )
+                        if (keyguardState.to == KeyguardState.PRIMARY_BOUNCER) {
+                            // An existing `id` means a transition is started, and calls to
+                            // `updateTransition` will control it until FINISHED or CANCELED
+                            var nextState =
+                                if (shadeModel.expansionAmount == 0f) {
+                                    TransitionState.FINISHED
+                                } else if (shadeModel.expansionAmount == 1f) {
+                                    TransitionState.CANCELED
+                                } else {
+                                    TransitionState.RUNNING
+                                }
+                            keyguardTransitionRepository.updateTransition(
+                                id,
+                                1f - shadeModel.expansionAmount,
+                                nextState,
                             )
+
+                            if (
+                                nextState == TransitionState.CANCELED ||
+                                    nextState == TransitionState.FINISHED
+                            ) {
+                                transitionId = null
+                            }
+
+                            // If canceled, just put the state back
+                            if (nextState == TransitionState.CANCELED) {
+                                keyguardTransitionRepository.startTransition(
+                                    TransitionInfo(
+                                        ownerName = name,
+                                        from = KeyguardState.PRIMARY_BOUNCER,
+                                        to = KeyguardState.LOCKSCREEN,
+                                        animator = getAnimator(0.milliseconds)
+                                    )
+                                )
+                            }
                         }
                     } else {
                         // TODO (b/251849525): Remove statusbarstate check when that state is
                         // integrated into KeyguardTransitionRepository
                         if (
-                            keyguardState == KeyguardState.LOCKSCREEN &&
+                            keyguardState.to == KeyguardState.LOCKSCREEN &&
                                 shadeModel.isUserDragging &&
+                                !isKeyguardUnlocked &&
                                 statusBarState == KEYGUARD
                         ) {
                             transitionId =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
index 2dc8fee..1fafd7f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
@@ -45,6 +45,7 @@
 
     override fun start() {
         listenForOccludedToLockscreen()
+        listenForOccludedToGone()
         listenForOccludedToDreaming()
         listenForOccludedToAodOrDozing()
     }
@@ -72,11 +73,22 @@
     private fun listenForOccludedToLockscreen() {
         scope.launch {
             keyguardInteractor.isKeyguardOccluded
-                .sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair)
-                .collect { (isOccluded, lastStartedKeyguardState) ->
+                .sample(
+                    combine(
+                        keyguardInteractor.isKeyguardShowing,
+                        keyguardTransitionInteractor.startedKeyguardTransitionStep,
+                        ::Pair
+                    ),
+                    ::toTriple
+                )
+                .collect { (isOccluded, isShowing, lastStartedKeyguardState) ->
                     // Occlusion signals come from the framework, and should interrupt any
                     // existing transition
-                    if (!isOccluded && lastStartedKeyguardState.to == KeyguardState.OCCLUDED) {
+                    if (
+                        !isOccluded &&
+                            isShowing &&
+                            lastStartedKeyguardState.to == KeyguardState.OCCLUDED
+                    ) {
                         keyguardTransitionRepository.startTransition(
                             TransitionInfo(
                                 name,
@@ -90,6 +102,38 @@
         }
     }
 
+    private fun listenForOccludedToGone() {
+        scope.launch {
+            keyguardInteractor.isKeyguardOccluded
+                .sample(
+                    combine(
+                        keyguardInteractor.isKeyguardShowing,
+                        keyguardTransitionInteractor.startedKeyguardTransitionStep,
+                        ::Pair
+                    ),
+                    ::toTriple
+                )
+                .collect { (isOccluded, isShowing, lastStartedKeyguardState) ->
+                    // Occlusion signals come from the framework, and should interrupt any
+                    // existing transition
+                    if (
+                        !isOccluded &&
+                            !isShowing &&
+                            lastStartedKeyguardState.to == KeyguardState.OCCLUDED
+                    ) {
+                        keyguardTransitionRepository.startTransition(
+                            TransitionInfo(
+                                name,
+                                KeyguardState.OCCLUDED,
+                                KeyguardState.GONE,
+                                getAnimator(),
+                            )
+                        )
+                    }
+                }
+        }
+    }
+
     private fun listenForOccludedToAodOrDozing() {
         scope.launch {
             keyguardInteractor.wakefulnessModel
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
index b59b413..94961cb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
@@ -17,6 +17,9 @@
 package com.android.systemui.keyguard.domain.interactor
 
 import android.animation.ValueAnimator
+import com.android.keyguard.KeyguardSecurityModel
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode.Password
+import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.animation.Interpolators
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
@@ -26,6 +29,8 @@
 import com.android.systemui.keyguard.shared.model.WakefulnessState
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.launch
@@ -37,7 +42,8 @@
     @Application private val scope: CoroutineScope,
     private val keyguardInteractor: KeyguardInteractor,
     private val keyguardTransitionRepository: KeyguardTransitionRepository,
-    private val keyguardTransitionInteractor: KeyguardTransitionInteractor
+    private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
+    private val keyguardSecurityModel: KeyguardSecurityModel,
 ) : TransitionInteractor(FromPrimaryBouncerTransitionInteractor::class.simpleName!!) {
 
     override fun start() {
@@ -93,31 +99,47 @@
     private fun listenForPrimaryBouncerToGone() {
         scope.launch {
             keyguardInteractor.isKeyguardGoingAway
-                .sample(keyguardTransitionInteractor.finishedKeyguardState) { a, b -> Pair(a, b) }
-                .collect { pair ->
-                    val (isKeyguardGoingAway, keyguardState) = pair
-                    if (isKeyguardGoingAway && keyguardState == KeyguardState.PRIMARY_BOUNCER) {
+                .sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair)
+                .collect { (isKeyguardGoingAway, lastStartedTransitionStep) ->
+                    if (
+                        isKeyguardGoingAway &&
+                            lastStartedTransitionStep.to == KeyguardState.PRIMARY_BOUNCER
+                    ) {
+                        val securityMode =
+                            keyguardSecurityModel.getSecurityMode(
+                                KeyguardUpdateMonitor.getCurrentUser()
+                            )
+                        // IME for password requires a slightly faster animation
+                        val duration =
+                            if (securityMode == Password) {
+                                TO_GONE_SHORT_DURATION
+                            } else {
+                                TO_GONE_DURATION
+                            }
                         keyguardTransitionRepository.startTransition(
                             TransitionInfo(
                                 ownerName = name,
                                 from = KeyguardState.PRIMARY_BOUNCER,
                                 to = KeyguardState.GONE,
-                                animator = getAnimator(),
-                            )
+                                animator = getAnimator(duration),
+                            ),
+                            resetIfCanceled = true,
                         )
                     }
                 }
         }
     }
 
-    private fun getAnimator(): ValueAnimator {
+    private fun getAnimator(duration: Duration = DEFAULT_DURATION): ValueAnimator {
         return ValueAnimator().apply {
             setInterpolator(Interpolators.LINEAR)
-            setDuration(TRANSITION_DURATION_MS)
+            setDuration(duration.inWholeMilliseconds)
         }
     }
 
     companion object {
-        private const val TRANSITION_DURATION_MS = 300L
+        private val DEFAULT_DURATION = 300.milliseconds
+        val TO_GONE_DURATION = 250.milliseconds
+        val TO_GONE_SHORT_DURATION = 200.milliseconds
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index d25aff0a..ec99049 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -33,7 +33,9 @@
 import com.android.systemui.keyguard.shared.model.DozeTransitionModel
 import com.android.systemui.keyguard.shared.model.StatusBarState
 import com.android.systemui.keyguard.shared.model.WakefulnessModel
+import com.android.systemui.keyguard.shared.model.WakefulnessModel.Companion.isWakingOrStartingToWake
 import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.delay
@@ -95,6 +97,9 @@
         awaitClose { commandQueue.removeCallback(callback) }
     }
 
+    /** The device wake/sleep state */
+    val wakefulnessModel: Flow<WakefulnessModel> = repository.wakefulness
+
     /**
      * Dozing and dreaming have overlapping events. If the doze state remains in FINISH, it means
      * that doze mode is not running and DREAMING is ok to commence.
@@ -109,6 +114,12 @@
                     isDreaming && isDozeOff(dozeTransitionModel.to)
                 }
             )
+            .sample(
+                wakefulnessModel,
+                { isAbleToDream, wakefulnessModel ->
+                    isAbleToDream && isWakingOrStartingToWake(wakefulnessModel)
+                }
+            )
             .flatMapLatest { isAbleToDream ->
                 flow {
                     delay(50)
@@ -119,6 +130,8 @@
 
     /** Whether the keyguard is showing or not. */
     val isKeyguardShowing: Flow<Boolean> = repository.isKeyguardShowing
+    /** Whether the keyguard is unlocked or not. */
+    val isKeyguardUnlocked: Flow<Boolean> = repository.isKeyguardUnlocked
     /** Whether the keyguard is occluded (covered by an activity). */
     val isKeyguardOccluded: Flow<Boolean> = repository.isKeyguardOccluded
     /** Whether the keyguard is going away. */
@@ -127,8 +140,6 @@
     val primaryBouncerShowing: Flow<Boolean> = bouncerRepository.primaryBouncerVisible
     /** Whether the alternate bouncer is showing or not. */
     val alternateBouncerShowing: Flow<Boolean> = bouncerRepository.alternateBouncerVisible
-    /** The device wake/sleep state */
-    val wakefulnessModel: Flow<WakefulnessModel> = repository.wakefulness
     /** Observable for the [StatusBarState] */
     val statusBarState: Flow<StatusBarState> = repository.statusBarState
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index bc3c720..1735b5d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -37,6 +37,7 @@
 import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePickerRepresentation
 import com.android.systemui.keyguard.shared.model.KeyguardSlotPickerRepresentation
 import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
+import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract
@@ -68,6 +69,7 @@
     private val featureFlags: FeatureFlags,
     private val repository: Lazy<KeyguardQuickAffordanceRepository>,
     private val launchAnimator: DialogLaunchAnimator,
+    private val logger: KeyguardQuickAffordancesMetricsLogger,
     private val devicePolicyManager: DevicePolicyManager,
     @Background private val backgroundDispatcher: CoroutineDispatcher,
 ) {
@@ -122,10 +124,12 @@
      * @param configKey The configuration key corresponding to the [KeyguardQuickAffordanceModel] of
      *   the affordance that was clicked
      * @param expandable An optional [Expandable] for the activity- or dialog-launch animation
+     * @param slotId The id of the lockscreen slot that the affordance is in
      */
     fun onQuickAffordanceTriggered(
         configKey: String,
         expandable: Expandable?,
+        slotId: String,
     ) {
         @Suppress("UNCHECKED_CAST")
         val config =
@@ -139,6 +143,7 @@
             Log.e(TAG, "Affordance config with key of \"$configKey\" not found!")
             return
         }
+        logger.logOnShortcutTriggered(slotId, configKey)
 
         when (val result = config.onTriggered(expandable)) {
             is KeyguardQuickAffordanceConfig.OnTriggeredResult.StartActivity ->
@@ -191,6 +196,7 @@
                 affordanceIds = selections,
             )
 
+        logger.logOnShortcutSelected(slotId, affordanceId)
         return true
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
index 51b0277..e650b9f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
@@ -61,7 +61,15 @@
         }
 
         scope.launch {
-            keyguardInteractor.isDreaming.collect { logger.log(TAG, VERBOSE, "isDreaming", it) }
+            keyguardInteractor.isAbleToDream.collect {
+                logger.log(TAG, VERBOSE, "isAbleToDream", it)
+            }
+        }
+
+        scope.launch {
+            keyguardInteractor.isKeyguardOccluded.collect {
+                logger.log(TAG, VERBOSE, "isOccluded", it)
+            }
         }
 
         scope.launch {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index 1b7da5b..3c0ec35 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -78,6 +78,10 @@
     val occludedToLockscreenTransition: Flow<TransitionStep> =
         repository.transition(OCCLUDED, LOCKSCREEN)
 
+    /** PRIMARY_BOUNCER->GONE transition information. */
+    val primaryBouncerToGoneTransition: Flow<TransitionStep> =
+        repository.transition(PRIMARY_BOUNCER, GONE)
+
     /**
      * AOD<->LOCKSCREEN transition information, mapped to dozeAmount range of AOD (1f) <->
      * Lockscreen (0f).
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/quickaffordance/KeyguardQuickAffordancesMetricsLogger.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/quickaffordance/KeyguardQuickAffordancesMetricsLogger.kt
new file mode 100644
index 0000000..0b0225a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/quickaffordance/KeyguardQuickAffordancesMetricsLogger.kt
@@ -0,0 +1,58 @@
+/*
+ * 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.keyguard.shared.quickaffordance
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.shared.system.SysUiStatsLog
+
+interface KeyguardQuickAffordancesMetricsLogger {
+
+    /**
+     * Logs shortcut Triggered
+     * @param slotId The id of the lockscreen slot that the affordance is in
+     * @param affordanceId The id of the lockscreen affordance
+     */
+    fun logOnShortcutTriggered(slotId: String, affordanceId: String)
+
+    /**
+     * Logs shortcut Selected
+     * @param slotId The id of the lockscreen slot that the affordance is in
+     * @param affordanceId The id of the lockscreen affordance
+     */
+    fun logOnShortcutSelected(slotId: String, affordanceId: String)
+
+}
+
+@SysUISingleton
+class KeyguardQuickAffordancesMetricsLoggerImpl : KeyguardQuickAffordancesMetricsLogger {
+
+    override fun logOnShortcutTriggered(slotId: String, affordanceId: String) {
+        SysUiStatsLog.write(
+                SysUiStatsLog.LOCKSCREEN_SHORTCUT_TRIGGERED,
+                slotId,
+                affordanceId,
+        )
+    }
+
+    override fun logOnShortcutSelected(slotId: String, affordanceId: String) {
+        SysUiStatsLog.write(
+                SysUiStatsLog.LOCKSCREEN_SHORTCUT_SELECTED,
+                slotId,
+                affordanceId,
+        )
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
index ca1e27c..38b9d50 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
@@ -47,6 +47,7 @@
         duration: Duration,
         onStep: (Float) -> Float,
         startTime: Duration = 0.milliseconds,
+        onStart: (() -> Unit)? = null,
         onCancel: (() -> Float)? = null,
         onFinish: (() -> Float)? = null,
         interpolator: Interpolator = LINEAR,
@@ -73,6 +74,7 @@
                 // the ViewModels of the last update
                 STARTED -> {
                     isComplete = false
+                    onStart?.invoke()
                     max(0f, min(1f, value))
                 }
                 // Always send a final value of 1. Because of rounding, [value] may never be
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
index 2a9060f6..d63636c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
@@ -502,6 +502,7 @@
                     KeyguardQuickAffordanceViewModel.OnClickedParameters(
                         configKey = configKey,
                         expandable = Expandable.fromView(view),
+                        slotId = viewModel.slotId,
                     )
                 )
             }
@@ -568,6 +569,7 @@
                     KeyguardQuickAffordanceViewModel.OnClickedParameters(
                         configKey = viewModel.configKey,
                         expandable = Expandable.fromView(view),
+                        slotId = viewModel.slotId,
                     )
                 )
             }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
index 7db567b..2337ffc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
@@ -31,6 +31,7 @@
 import com.android.systemui.keyguard.data.BouncerViewDelegate
 import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel
+import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.plugins.ActivityStarter
 import kotlinx.coroutines.awaitCancellation
@@ -44,6 +45,7 @@
     fun bind(
         view: ViewGroup,
         viewModel: KeyguardBouncerViewModel,
+        primaryBouncerToGoneTransitionViewModel: PrimaryBouncerToGoneTransitionViewModel,
         componentFactory: KeyguardBouncerComponent.Factory
     ) {
         // Builds the KeyguardSecurityContainerController from bouncer view group.
@@ -145,6 +147,12 @@
                     }
 
                     launch {
+                        primaryBouncerToGoneTransitionViewModel.bouncerAlpha.collect { alpha ->
+                            securityContainerController.setAlpha(alpha)
+                        }
+                    }
+
+                    launch {
                         viewModel.bouncerExpansionAmount
                             .filter { it == EXPANSION_VISIBLE }
                             .collect {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
index ab9e6a4..a8e3464 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
@@ -175,7 +175,8 @@
                     areQuickAffordancesFullyOpaque,
                     selectedPreviewSlotId,
                 ) { model, animateReveal, isFullyOpaque, selectedPreviewSlotId ->
-                    val isSelected = selectedPreviewSlotId == position.toSlotId()
+                    val slotId = position.toSlotId()
+                    val isSelected = selectedPreviewSlotId == slotId
                     model.toViewModel(
                         animateReveal = !previewMode.isInPreviewMode && animateReveal,
                         isClickable = isFullyOpaque && !previewMode.isInPreviewMode,
@@ -187,7 +188,8 @@
                             previewMode.isInPreviewMode &&
                                 previewMode.shouldHighlightSelectedAffordance &&
                                 !isSelected,
-                        forceInactive = previewMode.isInPreviewMode
+                        forceInactive = previewMode.isInPreviewMode,
+                        slotId = slotId,
                     )
                 }
                 .distinctUntilChanged()
@@ -200,6 +202,7 @@
         isSelected: Boolean,
         isDimmed: Boolean,
         forceInactive: Boolean,
+        slotId: String,
     ): KeyguardQuickAffordanceViewModel {
         return when (this) {
             is KeyguardQuickAffordanceModel.Visible ->
@@ -212,6 +215,7 @@
                         quickAffordanceInteractor.onQuickAffordanceTriggered(
                             configKey = parameters.configKey,
                             expandable = parameters.expandable,
+                            slotId = parameters.slotId,
                         )
                     },
                     isClickable = isClickable,
@@ -219,8 +223,11 @@
                     isSelected = isSelected,
                     useLongPress = quickAffordanceInteractor.useLongPress,
                     isDimmed = isDimmed,
+                    slotId = slotId,
                 )
-            is KeyguardQuickAffordanceModel.Hidden -> KeyguardQuickAffordanceViewModel()
+            is KeyguardQuickAffordanceModel.Hidden -> KeyguardQuickAffordanceViewModel(
+                slotId = slotId,
+            )
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt
index cb68a82..38d1db6d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt
@@ -32,9 +32,11 @@
     val isSelected: Boolean = false,
     val useLongPress: Boolean = false,
     val isDimmed: Boolean = false,
+    val slotId: String,
 ) {
     data class OnClickedParameters(
         val configKey: String,
         val expandable: Expandable?,
+        val slotId: String,
     )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
new file mode 100644
index 0000000..92038e2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
@@ -0,0 +1,69 @@
+/*
+ * 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.keyguard.ui.viewmodel
+
+import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor.Companion.TO_GONE_DURATION
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.statusbar.SysuiStatusBarStateController
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * Breaks down PRIMARY_BOUNCER->GONE transition into discrete steps for corresponding views to
+ * consume.
+ */
+@SysUISingleton
+class PrimaryBouncerToGoneTransitionViewModel
+@Inject
+constructor(
+    private val interactor: KeyguardTransitionInteractor,
+    private val statusBarStateController: SysuiStatusBarStateController,
+) {
+    private val transitionAnimation =
+        KeyguardTransitionAnimationFlow(
+            transitionDuration = TO_GONE_DURATION,
+            transitionFlow = interactor.primaryBouncerToGoneTransition,
+        )
+
+    private var leaveShadeOpen: Boolean = false
+
+    /** Bouncer container alpha */
+    val bouncerAlpha: Flow<Float> =
+        transitionAnimation.createFlow(
+            duration = 200.milliseconds,
+            onStep = { 1f - it },
+        )
+
+    /** Scrim behind alpha */
+    val scrimBehindAlpha: Flow<Float> =
+        transitionAnimation.createFlow(
+            duration = TO_GONE_DURATION,
+            interpolator = EMPHASIZED_ACCELERATE,
+            onStart = { leaveShadeOpen = statusBarStateController.leaveOpenOnKeyguardHide() },
+            onStep = {
+                if (leaveShadeOpen) {
+                    1f
+                } else {
+                    1f - it
+                }
+            },
+        )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 0dfb9b1..a233cdc 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -164,6 +164,8 @@
 import com.android.wm.shell.back.BackAnimation;
 import com.android.wm.shell.pip.Pip;
 
+import dagger.Lazy;
+
 import java.io.PrintWriter;
 import java.util.Locale;
 import java.util.Map;
@@ -173,8 +175,6 @@
 
 import javax.inject.Inject;
 
-import dagger.Lazy;
-
 /**
  * Contains logic for a navigation bar view.
  */
@@ -251,12 +251,6 @@
 
     private boolean mTransientShown;
     private boolean mTransientShownFromGestureOnSystemBar;
-    /**
-     * This is to indicate whether the navigation bar button is forced visible. This is true
-     * when the setup wizard is on display. When that happens, the window frame should be provided
-     * as insets size directly.
-     */
-    private boolean mIsButtonForceVisible;
     private int mNavBarMode = NAV_BAR_MODE_3BUTTON;
     private LightBarController mLightBarController;
     private final LightBarController mMainLightBarController;
@@ -670,8 +664,7 @@
         mView.setTouchHandler(mTouchHandler);
         setNavBarMode(mNavBarMode);
         mEdgeBackGestureHandler.setStateChangeCallback(mView::updateStates);
-        mEdgeBackGestureHandler.setButtonForceVisibleChangeCallback((forceVisible) -> {
-            mIsButtonForceVisible = forceVisible;
+        mEdgeBackGestureHandler.setButtonForcedVisibleChangeCallback((forceVisible) -> {
             repositionNavigationBar(mCurrentRotation);
         });
         mNavigationBarTransitions.addListener(this::onBarTransition);
@@ -1710,7 +1703,7 @@
 
     private InsetsFrameProvider[] getInsetsFrameProvider(int insetsHeight, Context userContext) {
         final InsetsFrameProvider navBarProvider;
-        if (insetsHeight != -1 && !mIsButtonForceVisible) {
+        if (insetsHeight != -1 && !mEdgeBackGestureHandler.isButtonForcedVisible()) {
             navBarProvider = new InsetsFrameProvider(
                     ITYPE_NAVIGATION_BAR, Insets.of(0, 0, 0, insetsHeight));
             // Use window frame for IME.
@@ -1733,41 +1726,26 @@
             bottomTappableProvider = new InsetsFrameProvider(ITYPE_BOTTOM_TAPPABLE_ELEMENT);
         }
 
-        if (!mEdgeBackGestureHandler.isHandlingGestures()) {
-            // 2/3 button navigation is on. Do not provide any gesture insets here. But need to keep
-            // the provider to support runtime update.
-            return new InsetsFrameProvider[] {
-                    navBarProvider,
-                    new InsetsFrameProvider(
-                            ITYPE_BOTTOM_MANDATORY_GESTURES, Insets.NONE),
-                    new InsetsFrameProvider(ITYPE_LEFT_GESTURES, InsetsFrameProvider.SOURCE_DISPLAY,
-                            Insets.NONE, null),
-                    new InsetsFrameProvider(ITYPE_RIGHT_GESTURES,
-                            InsetsFrameProvider.SOURCE_DISPLAY,
-                            Insets.NONE, null),
-                    bottomTappableProvider
-            };
-        } else {
-            // Gesture navigation
-            final int gestureHeight = userContext.getResources().getDimensionPixelSize(
-                    com.android.internal.R.dimen.navigation_bar_gesture_height);
-            final DisplayCutout cutout = userContext.getDisplay().getCutout();
-            final int safeInsetsLeft = cutout != null ? cutout.getSafeInsetLeft() : 0;
-            final int safeInsetsRight = cutout != null ? cutout.getSafeInsetRight() : 0;
-            return new InsetsFrameProvider[] {
-                    navBarProvider,
-                    new InsetsFrameProvider(
-                            ITYPE_BOTTOM_MANDATORY_GESTURES, Insets.of(0, 0, 0, gestureHeight)),
-                    new InsetsFrameProvider(ITYPE_LEFT_GESTURES, InsetsFrameProvider.SOURCE_DISPLAY,
-                            Insets.of(safeInsetsLeft
-                                    + mEdgeBackGestureHandler.getEdgeWidthLeft(), 0, 0, 0), null),
-                    new InsetsFrameProvider(ITYPE_RIGHT_GESTURES,
-                            InsetsFrameProvider.SOURCE_DISPLAY,
-                            Insets.of(0, 0, safeInsetsRight
-                                    + mEdgeBackGestureHandler.getEdgeWidthRight(), 0), null),
-                    bottomTappableProvider
-            };
-        }
+        final DisplayCutout cutout = userContext.getDisplay().getCutout();
+        final int safeInsetsLeft = cutout != null ? cutout.getSafeInsetLeft() : 0;
+        final int safeInsetsRight = cutout != null ? cutout.getSafeInsetRight() : 0;
+        final int gestureHeight = userContext.getResources().getDimensionPixelSize(
+                com.android.internal.R.dimen.navigation_bar_gesture_height);
+        final boolean handlingGesture = mEdgeBackGestureHandler.isHandlingGestures();
+        final int gestureInsetsLeft = handlingGesture
+                ? mEdgeBackGestureHandler.getEdgeWidthLeft() + safeInsetsLeft : 0;
+        final int gestureInsetsRight = handlingGesture
+                ? mEdgeBackGestureHandler.getEdgeWidthRight() + safeInsetsRight : 0;
+        return new InsetsFrameProvider[] {
+                navBarProvider,
+                new InsetsFrameProvider(
+                        ITYPE_BOTTOM_MANDATORY_GESTURES, Insets.of(0, 0, 0, gestureHeight)),
+                new InsetsFrameProvider(ITYPE_LEFT_GESTURES, InsetsFrameProvider.SOURCE_DISPLAY,
+                        Insets.of(gestureInsetsLeft, 0, 0, 0), null),
+                new InsetsFrameProvider(ITYPE_RIGHT_GESTURES, InsetsFrameProvider.SOURCE_DISPLAY,
+                        Insets.of(0, 0, gestureInsetsRight, 0), null),
+                bottomTappableProvider
+        };
     }
 
     private boolean canShowSecondaryHandle() {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index f28c275..163b6fa 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -178,7 +178,7 @@
     private final OverviewProxyService mOverviewProxyService;
     private final SysUiState mSysUiState;
     private Runnable mStateChangeCallback;
-    private Consumer<Boolean> mButtonForceVisibleCallback;
+    private Consumer<Boolean> mButtonForcedVisibleCallback;
 
     private final PluginManager mPluginManager;
     private final ProtoTracer mProtoTracer;
@@ -246,7 +246,7 @@
     private boolean mGestureBlockingActivityRunning;
     private boolean mIsNewBackAffordanceEnabled;
     private boolean mIsTrackpadGestureBackEnabled;
-    private boolean mIsButtonForceVisible;
+    private boolean mIsButtonForcedVisible;
 
     private InputMonitor mInputMonitor;
     private InputChannelCompat.InputEventReceiver mInputEventReceiver;
@@ -413,8 +413,8 @@
         mStateChangeCallback = callback;
     }
 
-    public void setButtonForceVisibleChangeCallback(Consumer<Boolean> callback) {
-        mButtonForceVisibleCallback = callback;
+    public void setButtonForcedVisibleChangeCallback(Consumer<Boolean> callback) {
+        mButtonForcedVisibleCallback = callback;
     }
 
     public int getEdgeWidthLeft() {
@@ -429,13 +429,14 @@
         Resources res = mNavigationModeController.getCurrentUserContext().getResources();
         mEdgeWidthLeft = mGestureNavigationSettingsObserver.getLeftSensitivity(res);
         mEdgeWidthRight = mGestureNavigationSettingsObserver.getRightSensitivity(res);
-        final boolean previousForceVisible = mIsButtonForceVisible;
-        mIsButtonForceVisible =
+        final boolean previousForcedVisible = mIsButtonForcedVisible;
+        mIsButtonForcedVisible =
                 mGestureNavigationSettingsObserver.areNavigationButtonForcedVisible();
-        if (previousForceVisible != mIsButtonForceVisible && mButtonForceVisibleCallback != null) {
-            mButtonForceVisibleCallback.accept(mIsButtonForceVisible);
+        if (previousForcedVisible != mIsButtonForcedVisible
+                && mButtonForcedVisibleCallback != null) {
+            mButtonForcedVisibleCallback.accept(mIsButtonForcedVisible);
         }
-        mIsBackGestureAllowed = !mIsButtonForceVisible;
+        mIsBackGestureAllowed = !mIsButtonForcedVisible;
 
         final DisplayMetrics dm = res.getDisplayMetrics();
         final float defaultGestureHeight = res.getDimension(
@@ -635,6 +636,10 @@
         return mIsEnabled && mIsBackGestureAllowed;
     }
 
+    public boolean isButtonForcedVisible() {
+        return mIsButtonForcedVisible;
+    }
+
     /**
      * Update the PiP bounds, used for exclusion calculation.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt
index acc537a..2fa8f9a 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt
@@ -17,7 +17,7 @@
 
 import com.android.systemui.notetask.quickaffordance.NoteTaskQuickAffordanceConfig
 import com.android.systemui.notetask.shortcut.LaunchNoteTaskActivity
-import com.android.systemui.screenshot.AppClipsTrampolineActivity
+import com.android.systemui.screenshot.appclips.AppClipsTrampolineActivity
 
 /**
  * Supported entry points for [NoteTaskController.showNoteTask].
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/CreateNoteTaskShortcutActivity.kt b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/CreateNoteTaskShortcutActivity.kt
index 8ced4646..5c59532 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/CreateNoteTaskShortcutActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/CreateNoteTaskShortcutActivity.kt
@@ -17,8 +17,10 @@
 package com.android.systemui.notetask.shortcut
 
 import android.app.Activity
+import android.app.role.RoleManager
 import android.content.Intent
 import android.os.Bundle
+import android.os.PersistableBundle
 import androidx.activity.ComponentActivity
 import androidx.annotation.DrawableRes
 import androidx.core.content.pm.ShortcutInfoCompat
@@ -36,7 +38,11 @@
  *   href="https://developer.android.com/develop/ui/views/launch/shortcuts/creating-shortcuts#custom-pinned">Creating
  *   a custom shortcut activity</a>
  */
-class CreateNoteTaskShortcutActivity @Inject constructor() : ComponentActivity() {
+class CreateNoteTaskShortcutActivity
+@Inject
+constructor(
+    private val roleManager: RoleManager,
+) : ComponentActivity() {
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
@@ -59,12 +65,19 @@
         intent: Intent,
         @DrawableRes iconResource: Int,
     ): Intent {
+        val extras = PersistableBundle()
+
+        roleManager.getRoleHoldersAsUser(RoleManager.ROLE_NOTES, user).firstOrNull()?.let { name ->
+            extras.putString(EXTRA_SHORTCUT_BADGE_OVERRIDE_PACKAGE, name)
+        }
+
         val shortcutInfo =
             ShortcutInfoCompat.Builder(this, id)
                 .setIntent(intent)
                 .setShortLabel(shortLabel)
                 .setLongLived(true)
                 .setIcon(IconCompat.createWithResource(this, iconResource))
+                .setExtras(extras)
                 .build()
 
         return ShortcutManagerCompat.createShortcutResultIntent(
@@ -75,5 +88,16 @@
 
     private companion object {
         private const val SHORTCUT_ID = "note-task-shortcut-id"
+
+        /**
+         * Shortcut extra which can point to a package name and can be used to indicate an alternate
+         * badge info. Launcher only reads this if the shortcut comes from a system app.
+         *
+         * Duplicated from [com.android.launcher3.icons.IconCache].
+         *
+         * @see com.android.launcher3.icons.IconCache.EXTRA_SHORTCUT_BADGE_OVERRIDE_PACKAGE
+         */
+        private const val EXTRA_SHORTCUT_BADGE_OVERRIDE_PACKAGE =
+            "extra_shortcut_badge_override_package"
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 25ff308b..019ca52 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -631,7 +631,9 @@
         final NavigationBarView navBarView =
                 mNavBarControllerLazy.get().getNavigationBarView(mContext.getDisplayId());
         final NotificationPanelViewController panelController =
-                mCentralSurfacesOptionalLazy.get().get().getNotificationPanelViewController();
+                mCentralSurfacesOptionalLazy.get()
+                        .map(CentralSurfaces::getNotificationPanelViewController)
+                        .orElse(null);
         if (SysUiState.DEBUG) {
             Log.d(TAG_OPS, "Updating sysui state flags: navBarFragment=" + navBarFragment
                     + " navBarView=" + navBarView + " panelController=" + panelController);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java
index c8c1337..7cfe232 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java
@@ -57,7 +57,8 @@
 
 import javax.inject.Inject;
 
-class ImageExporter {
+/** A class to help with exporting screenshot to storage. */
+public class ImageExporter {
     private static final String TAG = LogConfig.logTag(ImageExporter.class);
 
     static final Duration PENDING_ENTRY_TTL = Duration.ofHours(24);
@@ -90,7 +91,7 @@
     private final FeatureFlags mFlags;
 
     @Inject
-    ImageExporter(ContentResolver resolver, FeatureFlags flags) {
+    public ImageExporter(ContentResolver resolver, FeatureFlags flags) {
         mResolver = resolver;
         mFlags = flags;
     }
@@ -148,7 +149,7 @@
      *
      * @return a listenable future result
      */
-    ListenableFuture<Result> export(Executor executor, UUID requestId, Bitmap bitmap,
+    public ListenableFuture<Result> export(Executor executor, UUID requestId, Bitmap bitmap,
             UserHandle owner) {
         return export(executor, requestId, bitmap, ZonedDateTime.now(), owner);
     }
@@ -181,13 +182,14 @@
         );
     }
 
-    static class Result {
-        Uri uri;
-        UUID requestId;
-        String fileName;
-        long timestamp;
-        CompressFormat format;
-        boolean published;
+    /** The result returned by the task exporting screenshots to storage. */
+    public static class Result {
+        public Uri uri;
+        public UUID requestId;
+        public String fileName;
+        public long timestamp;
+        public CompressFormat format;
+        public boolean published;
 
         @Override
         public String toString() {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
index fc94aed..7a62bae 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
@@ -93,13 +93,7 @@
     @UiEvent(doc = "User has discarded the result of a long screenshot")
     SCREENSHOT_LONG_SCREENSHOT_EXIT(911),
     @UiEvent(doc = "A screenshot has been taken and saved to work profile")
-    SCREENSHOT_SAVED_TO_WORK_PROFILE(1240),
-    @UiEvent(doc = "Notes application triggered the screenshot for notes")
-    SCREENSHOT_FOR_NOTE_TRIGGERED(1308),
-    @UiEvent(doc = "User accepted the screenshot to be sent to the notes app")
-    SCREENSHOT_FOR_NOTE_ACCEPTED(1309),
-    @UiEvent(doc = "User cancelled the screenshot for notes app flow")
-    SCREENSHOT_FOR_NOTE_CANCELLED(1310);
+    SCREENSHOT_SAVED_TO_WORK_PROFILE(1240);
 
     private final int mId;
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java
index 3133924..4756cc8 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java
@@ -14,15 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.appclips;
 
-import static com.android.systemui.screenshot.AppClipsTrampolineActivity.ACTION_FINISH_FROM_TRAMPOLINE;
-import static com.android.systemui.screenshot.AppClipsTrampolineActivity.EXTRA_CALLING_PACKAGE_NAME;
-import static com.android.systemui.screenshot.AppClipsTrampolineActivity.EXTRA_RESULT_RECEIVER;
-import static com.android.systemui.screenshot.AppClipsTrampolineActivity.EXTRA_SCREENSHOT_URI;
-import static com.android.systemui.screenshot.AppClipsTrampolineActivity.PERMISSION_SELF;
-import static com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_FOR_NOTE_ACCEPTED;
-import static com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_FOR_NOTE_CANCELLED;
+import static com.android.systemui.screenshot.appclips.AppClipsEvent.SCREENSHOT_FOR_NOTE_ACCEPTED;
+import static com.android.systemui.screenshot.appclips.AppClipsEvent.SCREENSHOT_FOR_NOTE_CANCELLED;
+import static com.android.systemui.screenshot.appclips.AppClipsTrampolineActivity.ACTION_FINISH_FROM_TRAMPOLINE;
+import static com.android.systemui.screenshot.appclips.AppClipsTrampolineActivity.EXTRA_CALLING_PACKAGE_NAME;
+import static com.android.systemui.screenshot.appclips.AppClipsTrampolineActivity.EXTRA_RESULT_RECEIVER;
+import static com.android.systemui.screenshot.appclips.AppClipsTrampolineActivity.EXTRA_SCREENSHOT_URI;
+import static com.android.systemui.screenshot.appclips.AppClipsTrampolineActivity.PERMISSION_SELF;
 
 import android.app.Activity;
 import android.content.BroadcastReceiver;
@@ -52,6 +52,8 @@
 import com.android.internal.logging.UiEventLogger.UiEventEnum;
 import com.android.settingslib.Utils;
 import com.android.systemui.R;
+import com.android.systemui.screenshot.CropView;
+import com.android.systemui.screenshot.MagnifierView;
 import com.android.systemui.settings.UserTracker;
 
 import javax.inject.Inject;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsCrossProcessHelper.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsCrossProcessHelper.java
index 65fb4c9..e1619dc 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsCrossProcessHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsCrossProcessHelper.java
@@ -32,12 +32,12 @@
 
 /** An intermediary singleton object to help communicating with the cross process service. */
 @SysUISingleton
-public class AppClipsCrossProcessHelper {
+class AppClipsCrossProcessHelper {
 
     private final ServiceConnector<IAppClipsScreenshotHelperService> mProxyConnector;
 
     @Inject
-    public AppClipsCrossProcessHelper(@Application Context context) {
+    AppClipsCrossProcessHelper(@Application Context context) {
         mProxyConnector = new ServiceConnector.Impl<IAppClipsScreenshotHelperService>(context,
                 new Intent(context, AppClipsScreenshotHelperService.class),
                 Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY
@@ -52,7 +52,7 @@
      * pass around but not a {@link Bitmap}.
      */
     @Nullable
-    public Bitmap takeScreenshot() {
+    Bitmap takeScreenshot() {
         try {
             AndroidFuture<ScreenshotHardwareBufferInternal> future =
                     mProxyConnector.postForResult(
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsEvent.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsEvent.java
new file mode 100644
index 0000000..7a085b9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsEvent.java
@@ -0,0 +1,40 @@
+/*
+ * 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.screenshot.appclips;
+
+import com.android.internal.logging.UiEvent;
+import com.android.internal.logging.UiEventLogger;
+
+enum AppClipsEvent implements UiEventLogger.UiEventEnum {
+
+    @UiEvent(doc = "Notes application triggered the screenshot for notes")
+    SCREENSHOT_FOR_NOTE_TRIGGERED(1308),
+    @UiEvent(doc = "User accepted the screenshot to be sent to the notes app")
+    SCREENSHOT_FOR_NOTE_ACCEPTED(1309),
+    @UiEvent(doc = "User cancelled the screenshot for notes app flow")
+    SCREENSHOT_FOR_NOTE_CANCELLED(1310);
+
+    private final int mId;
+
+    AppClipsEvent(int id) {
+        mId = id;
+    }
+
+    @Override
+    public int getId() {
+        return mId;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsScreenshotHelperService.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsScreenshotHelperService.java
index 6f8c365..83ff020 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsScreenshotHelperService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsScreenshotHelperService.java
@@ -24,7 +24,6 @@
 
 import androidx.annotation.Nullable;
 
-import com.android.systemui.screenshot.AppClipsActivity;
 import com.android.wm.shell.bubbles.Bubbles;
 
 import java.util.Optional;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivity.java
index eda38e4..3cb1a34 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivity.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.appclips;
 
 import static android.content.Intent.CAPTURE_CONTENT_FOR_NOTE_BLOCKED_BY_ADMIN;
 import static android.content.Intent.CAPTURE_CONTENT_FOR_NOTE_FAILED;
@@ -24,7 +24,7 @@
 import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
 
 import static com.android.systemui.flags.Flags.SCREENSHOT_APP_CLIPS;
-import static com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_FOR_NOTE_TRIGGERED;
+import static com.android.systemui.screenshot.appclips.AppClipsEvent.SCREENSHOT_FOR_NOTE_TRIGGERED;
 
 import android.app.Activity;
 import android.app.admin.DevicePolicyManager;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsViewModel.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsViewModel.java
index b2910fd..4cbca28a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsViewModel.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsViewModel.java
@@ -14,12 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.appclips;
 
 import static android.content.Intent.CAPTURE_CONTENT_FOR_NOTE_FAILED;
 
-import static androidx.annotation.VisibleForTesting.PACKAGE_PRIVATE;
-
 import android.content.Intent;
 import android.graphics.Bitmap;
 import android.graphics.HardwareRenderer;
@@ -31,7 +29,6 @@
 import android.os.Process;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.VisibleForTesting;
 import androidx.lifecycle.LiveData;
 import androidx.lifecycle.MutableLiveData;
 import androidx.lifecycle.ViewModel;
@@ -39,11 +36,10 @@
 
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.screenshot.appclips.AppClipsCrossProcessHelper;
+import com.android.systemui.screenshot.ImageExporter;
 
 import com.google.common.util.concurrent.ListenableFuture;
 
-import java.time.ZonedDateTime;
 import java.util.UUID;
 import java.util.concurrent.CancellationException;
 import java.util.concurrent.ExecutionException;
@@ -52,8 +48,7 @@
 import javax.inject.Inject;
 
 /** A {@link ViewModel} to help with the App Clips screenshot flow. */
-@VisibleForTesting(otherwise = PACKAGE_PRIVATE)
-public final class AppClipsViewModel extends ViewModel {
+final class AppClipsViewModel extends ViewModel {
 
     private final AppClipsCrossProcessHelper mAppClipsCrossProcessHelper;
     private final ImageExporter mImageExporter;
@@ -80,8 +75,7 @@
     }
 
     /** Grabs a screenshot and updates the {@link Bitmap} set in screenshot {@link LiveData}. */
-    @VisibleForTesting(otherwise = PACKAGE_PRIVATE)
-    public void performScreenshot() {
+    void performScreenshot() {
         mBgExecutor.execute(() -> {
             Bitmap screenshot = mAppClipsCrossProcessHelper.takeScreenshot();
             mMainExecutor.execute(() -> {
@@ -95,14 +89,12 @@
     }
 
     /** Returns a {@link LiveData} that holds the captured screenshot. */
-    @VisibleForTesting(otherwise = PACKAGE_PRIVATE)
-    public LiveData<Bitmap> getScreenshot() {
+    LiveData<Bitmap> getScreenshot() {
         return mScreenshotLiveData;
     }
 
     /** Returns a {@link LiveData} that holds the {@link Uri} where screenshot is saved. */
-    @VisibleForTesting(otherwise = PACKAGE_PRIVATE)
-    public LiveData<Uri> getResultLiveData() {
+    LiveData<Uri> getResultLiveData() {
         return mResultLiveData;
     }
 
@@ -110,8 +102,7 @@
      * Returns a {@link LiveData} that holds the error codes for
      * {@link Intent#EXTRA_CAPTURE_CONTENT_FOR_NOTE_STATUS_CODE}.
      */
-    @VisibleForTesting(otherwise = PACKAGE_PRIVATE)
-    public LiveData<Integer> getErrorLiveData() {
+    LiveData<Integer> getErrorLiveData() {
         return mErrorLiveData;
     }
 
@@ -119,8 +110,7 @@
      * Saves the provided {@link Drawable} to storage then informs the result {@link Uri} to
      * {@link LiveData}.
      */
-    @VisibleForTesting(otherwise = PACKAGE_PRIVATE)
-    public void saveScreenshotThenFinish(Drawable screenshotDrawable, Rect bounds) {
+    void saveScreenshotThenFinish(Drawable screenshotDrawable, Rect bounds) {
         mBgExecutor.execute(() -> {
             // Render the screenshot bitmap in background.
             Bitmap screenshotBitmap = renderBitmap(screenshotDrawable, bounds);
@@ -128,7 +118,7 @@
             // Export and save the screenshot in background.
             // TODO(b/267310185): Save to work profile UserHandle.
             ListenableFuture<ImageExporter.Result> exportFuture = mImageExporter.export(
-                    mBgExecutor, UUID.randomUUID(), screenshotBitmap, ZonedDateTime.now(),
+                    mBgExecutor, UUID.randomUUID(), screenshotBitmap,
                     Process.myUserHandle());
 
             // Get the result and update state on main thread.
@@ -160,8 +150,7 @@
     }
 
     /** Helper factory to help with injecting {@link AppClipsViewModel}. */
-    @VisibleForTesting(otherwise = PACKAGE_PRIVATE)
-    public static final class Factory implements ViewModelProvider.Factory {
+    static final class Factory implements ViewModelProvider.Factory {
 
         private final AppClipsCrossProcessHelper mAppClipsCrossProcessHelper;
         private final ImageExporter mImageExporter;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/ScreenshotHardwareBufferInternal.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/ScreenshotHardwareBufferInternal.java
index 3b107f1..1e53ebb 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/ScreenshotHardwareBufferInternal.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/ScreenshotHardwareBufferInternal.java
@@ -28,7 +28,7 @@
  * An internal version of {@link ScreenshotHardwareBuffer} that helps with parceling the information
  * necessary for creating a {@link Bitmap}.
  */
-public class ScreenshotHardwareBufferInternal implements Parcelable {
+class ScreenshotHardwareBufferInternal implements Parcelable {
 
     public static final Creator<ScreenshotHardwareBufferInternal> CREATOR =
             new Creator<>() {
@@ -45,7 +45,7 @@
     private final HardwareBuffer mHardwareBuffer;
     private final ParcelableColorSpace mParcelableColorSpace;
 
-    public ScreenshotHardwareBufferInternal(
+    ScreenshotHardwareBufferInternal(
             ScreenshotHardwareBuffer screenshotHardwareBuffer) {
         mHardwareBuffer = screenshotHardwareBuffer.getHardwareBuffer();
         mParcelableColorSpace = new ParcelableColorSpace(
@@ -65,7 +65,7 @@
      * {@link Bitmap#wrapHardwareBuffer(HardwareBuffer, ColorSpace)} and
      * {@link HardwareBuffer#close()} for more information.
      */
-    public Bitmap createBitmapThenCloseBuffer() {
+    Bitmap createBitmapThenCloseBuffer() {
         Bitmap bitmap = Bitmap.wrapHardwareBuffer(mHardwareBuffer,
                 mParcelableColorSpace.getColorSpace());
         mHardwareBuffer.close();
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index 87350b46..c130b39 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -45,6 +45,7 @@
 import com.android.systemui.keyguard.shared.model.TransitionStep;
 import com.android.systemui.keyguard.ui.binder.KeyguardBouncerViewBinder;
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel;
+import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
 import com.android.systemui.statusbar.DragDownHelper;
 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
 import com.android.systemui.statusbar.NotificationInsetsController;
@@ -133,7 +134,8 @@
             KeyguardBouncerViewModel keyguardBouncerViewModel,
             KeyguardBouncerComponent.Factory keyguardBouncerComponentFactory,
             AlternateBouncerInteractor alternateBouncerInteractor,
-            KeyguardTransitionInteractor keyguardTransitionInteractor
+            KeyguardTransitionInteractor keyguardTransitionInteractor,
+            PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel
     ) {
         mLockscreenShadeTransitionController = transitionController;
         mFalsingCollector = falsingCollector;
@@ -160,6 +162,7 @@
         KeyguardBouncerViewBinder.bind(
                 mView.findViewById(R.id.keyguard_bouncer_container),
                 keyguardBouncerViewModel,
+                primaryBouncerToGoneTransitionViewModel,
                 keyguardBouncerComponentFactory);
 
         collectFlow(mView, keyguardTransitionInteractor.getLockscreenToDreamingTransition(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index d6dc671..664d61a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -3747,6 +3747,12 @@
     @Override
     public void notifyBiometricAuthModeChanged() {
         mDozeServiceHost.updateDozing();
+        if (mBiometricUnlockController.getMode()
+                == BiometricUnlockController.MODE_DISMISS_BOUNCER) {
+            // Don't update the scrim controller at this time, in favor of the transition repository
+            // updating the scrim
+            return;
+        }
         updateScrimController();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index 753032c..3268032 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -59,7 +59,7 @@
 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 import com.android.systemui.statusbar.phone.fragment.StatusBarIconBlocklistKt;
-import com.android.systemui.statusbar.phone.fragment.StatusBarSystemEventAnimator;
+import com.android.systemui.statusbar.phone.fragment.StatusBarSystemEventDefaultAnimator;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -75,6 +75,8 @@
 
 import javax.inject.Inject;
 
+import kotlin.Unit;
+
 /** View Controller for {@link com.android.systemui.statusbar.phone.KeyguardStatusBarView}. */
 public class KeyguardStatusBarViewController extends ViewController<KeyguardStatusBarView> {
     private static final String TAG = "KeyguardStatusBarViewController";
@@ -123,7 +125,8 @@
                 public void onDensityOrFontScaleChanged() {
                     mView.loadDimens();
                     // The animator is dependent on resources for offsets
-                    mSystemEventAnimator = new StatusBarSystemEventAnimator(mView, getResources());
+                    mSystemEventAnimator =
+                            getSystemEventAnimator(mSystemEventAnimator.isAnimationRunning());
                 }
 
                 @Override
@@ -248,7 +251,8 @@
     private int mStatusBarState;
     private boolean mDozing;
     private boolean mShowingKeyguardHeadsUp;
-    private StatusBarSystemEventAnimator mSystemEventAnimator;
+    private StatusBarSystemEventDefaultAnimator mSystemEventAnimator;
+    private float mSystemEventAnimatorAlpha = 1;
 
     /**
      * The alpha value to be set on the View. If -1, this value is to be ignored.
@@ -324,7 +328,7 @@
 
         mView.setKeyguardUserAvatarEnabled(
                 !mStatusBarUserChipViewModel.getChipEnabled());
-        mSystemEventAnimator = new StatusBarSystemEventAnimator(mView, r);
+        mSystemEventAnimator = getSystemEventAnimator(/* isAnimationRunning */ false);
 
         mDisableStateTracker = new DisableStateTracker(
                 /* mask1= */ DISABLE_SYSTEM_INFO,
@@ -480,6 +484,10 @@
                     * (1.0f - mKeyguardHeadsUpShowingAmount);
         }
 
+        if (mSystemEventAnimator.isAnimationRunning()) {
+            newAlpha = Math.min(newAlpha, mSystemEventAnimatorAlpha);
+        }
+
         boolean hideForBypass =
                 mFirstBypassAttempt && mKeyguardUpdateMonitor.shouldListenForFace()
                         || mDelayShowingKeyguardStatusBar;
@@ -488,7 +496,7 @@
                         && !mDozing
                         && !hideForBypass
                         && !mDisableStateTracker.isDisabled()
-                ? View.VISIBLE : View.INVISIBLE;
+                        ? View.VISIBLE : View.INVISIBLE;
 
         updateViewState(newAlpha, newVisibility);
     }
@@ -614,4 +622,15 @@
             updateBlockedIcons();
         }
     };
+
+    private StatusBarSystemEventDefaultAnimator getSystemEventAnimator(boolean isAnimationRunning) {
+        return new StatusBarSystemEventDefaultAnimator(getResources(), (alpha) -> {
+            mSystemEventAnimatorAlpha = alpha;
+            updateViewState();
+            return Unit.INSTANCE;
+        }, (translationX) -> {
+            mView.setTranslationX(translationX);
+            return Unit.INSTANCE;
+        }, isAnimationRunning);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index fb8bf52..9fb942c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
+
 import static java.lang.Float.isNaN;
 
 import android.animation.Animator;
@@ -53,9 +55,14 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
 import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants;
+import com.android.systemui.keyguard.shared.model.TransitionState;
+import com.android.systemui.keyguard.shared.model.TransitionStep;
+import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
 import com.android.systemui.scrim.ScrimView;
 import com.android.systemui.shade.NotificationPanelViewController;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.stack.ViewState;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -71,6 +78,8 @@
 
 import javax.inject.Inject;
 
+import kotlinx.coroutines.CoroutineDispatcher;
+
 /**
  * Controls both the scrim behind the notifications and in front of the notifications (when a
  * security method gets shown).
@@ -197,6 +206,7 @@
     private final ScreenOffAnimationController mScreenOffAnimationController;
     private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
     private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+    private final SysuiStatusBarStateController mStatusBarStateController;
 
     private GradientColors mColors;
     private boolean mNeedsDrawableColorUpdate;
@@ -251,6 +261,20 @@
     private boolean mWakeLockHeld;
     private boolean mKeyguardOccluded;
 
+    private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
+    private CoroutineDispatcher mMainDispatcher;
+    private boolean mIsBouncerToGoneTransitionRunning = false;
+    private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel;
+    private final Consumer<Float> mScrimAlphaConsumer =
+            (Float alpha) -> {
+                mScrimInFront.setViewAlpha(mInFrontAlpha);
+                mNotificationsScrim.setViewAlpha(mNotificationsAlpha);
+                mBehindAlpha = alpha;
+                mScrimBehind.setViewAlpha(alpha);
+            };
+
+    Consumer<TransitionStep> mPrimaryBouncerToGoneTransition;
+
     @Inject
     public ScrimController(
             LightBarController lightBarController,
@@ -265,13 +289,18 @@
             @Main Executor mainExecutor,
             ScreenOffAnimationController screenOffAnimationController,
             KeyguardUnlockAnimationController keyguardUnlockAnimationController,
-            StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
+            StatusBarKeyguardViewManager statusBarKeyguardViewManager,
+            PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel,
+            KeyguardTransitionInteractor keyguardTransitionInteractor,
+            SysuiStatusBarStateController sysuiStatusBarStateController,
+            @Main CoroutineDispatcher mainDispatcher) {
         mScrimStateListener = lightBarController::setScrimState;
         mDefaultScrimAlpha = BUSY_SCRIM_ALPHA;
 
         mKeyguardStateController = keyguardStateController;
         mDarkenWhileDragging = !mKeyguardStateController.canDismissLockScreen();
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+        mStatusBarStateController = sysuiStatusBarStateController;
         mKeyguardVisibilityCallback = new KeyguardVisibilityCallback();
         mHandler = handler;
         mMainExecutor = mainExecutor;
@@ -304,6 +333,9 @@
             }
         });
         mColors = new GradientColors();
+        mPrimaryBouncerToGoneTransitionViewModel = primaryBouncerToGoneTransitionViewModel;
+        mKeyguardTransitionInteractor = keyguardTransitionInteractor;
+        mMainDispatcher = mainDispatcher;
     }
 
     /**
@@ -343,6 +375,33 @@
         for (ScrimState state : ScrimState.values()) {
             state.prepare(state);
         }
+
+        // Directly control transition to UNLOCKED scrim state from PRIMARY_BOUNCER, and make sure
+        // to report back that keyguard has faded away. This fixes cases where the scrim state was
+        // rapidly switching on unlock, due to shifts in state in CentralSurfacesImpl
+        mPrimaryBouncerToGoneTransition =
+                (TransitionStep step) -> {
+                    TransitionState state = step.getTransitionState();
+
+                    mIsBouncerToGoneTransitionRunning = state == TransitionState.RUNNING;
+
+                    if (state == TransitionState.STARTED) {
+                        setExpansionAffectsAlpha(false);
+                        transitionTo(ScrimState.UNLOCKED);
+                    }
+
+                    if (state == TransitionState.FINISHED || state == TransitionState.CANCELED) {
+                        setExpansionAffectsAlpha(true);
+                        if (mKeyguardStateController.isKeyguardFadingAway()) {
+                            mStatusBarKeyguardViewManager.onKeyguardFadedAway();
+                        }
+                    }
+                };
+
+        collectFlow(behindScrim, mKeyguardTransitionInteractor.getPrimaryBouncerToGoneTransition(),
+                mPrimaryBouncerToGoneTransition, mMainDispatcher);
+        collectFlow(behindScrim, mPrimaryBouncerToGoneTransitionViewModel.getScrimBehindAlpha(),
+                mScrimAlphaConsumer, mMainDispatcher);
     }
 
     // TODO(b/270984686) recompute scrim height accurately, based on shade contents.
@@ -372,6 +431,11 @@
     }
 
     public void transitionTo(ScrimState state, Callback callback) {
+        if (mIsBouncerToGoneTransitionRunning) {
+            Log.i(TAG, "Skipping transition to: " + state
+                    + " while mIsBouncerToGoneTransitionRunning");
+            return;
+        }
         if (state == mState) {
             // Call the callback anyway, unless it's already enqueued
             if (callback != null && mCallback != callback) {
@@ -1049,7 +1113,8 @@
             mBehindAlpha = 1;
         }
         // Prevent notification scrim flicker when transitioning away from keyguard.
-        if (mKeyguardStateController.isKeyguardGoingAway()) {
+        if (mKeyguardStateController.isKeyguardGoingAway()
+                && !mStatusBarStateController.leaveOpenOnKeyguardHide()) {
             mNotificationsAlpha = 0;
         }
 
@@ -1138,7 +1203,9 @@
             Trace.traceCounter(Trace.TRACE_TAG_APP, getScrimName(scrimView) + "_tint",
                     Color.alpha(tint));
             scrimView.setTint(tint);
-            scrimView.setViewAlpha(alpha);
+            if (!mIsBouncerToGoneTransitionRunning) {
+                scrimView.setViewAlpha(alpha);
+            }
         } else {
             scrim.setAlpha(alpha);
         }
@@ -1486,6 +1553,9 @@
     }
 
     public void setKeyguardOccluded(boolean keyguardOccluded) {
+        if (mKeyguardOccluded == keyguardOccluded) {
+            return;
+        }
         mKeyguardOccluded = keyguardOccluded;
         updateScrims();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarSystemEventAnimator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarSystemEventAnimator.kt
index c04ea36..5903fa3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarSystemEventAnimator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarSystemEventAnimator.kt
@@ -26,19 +26,39 @@
 import com.android.systemui.statusbar.events.STATUS_BAR_X_MOVE_OUT
 import com.android.systemui.statusbar.events.SystemStatusAnimationCallback
 import com.android.systemui.util.animation.AnimationUtil.Companion.frames
+import com.android.systemui.util.doOnCancel
+import com.android.systemui.util.doOnEnd
+
+/**
+ * An implementation of [StatusBarSystemEventDefaultAnimator], applying the onAlphaChanged and
+ * onTranslationXChanged callbacks directly to the provided animatedView.
+ */
+class StatusBarSystemEventAnimator @JvmOverloads constructor(
+        val animatedView: View,
+        resources: Resources,
+        isAnimationRunning: Boolean = false
+) : StatusBarSystemEventDefaultAnimator(
+        resources = resources,
+        onAlphaChanged = animatedView::setAlpha,
+        onTranslationXChanged = animatedView::setTranslationX,
+        isAnimationRunning = isAnimationRunning
+)
 
 /**
  * Tied directly to [SystemStatusAnimationScheduler]. Any StatusBar-like thing (keyguard, collapsed
- * status bar fragment), can just feed this an animatable view to get the default system status
- * animation.
+ * status bar fragment), can use this Animator to get the default system status animation. It simply
+ * needs to implement the onAlphaChanged and onTranslationXChanged callbacks.
  *
  * This animator relies on resources, and should be recreated whenever resources are updated. While
  * this class could be used directly as the animation callback, it's probably best to forward calls
  * to it so that it can be recreated at any moment without needing to remove/add callback.
  */
-class StatusBarSystemEventAnimator(
-    val animatedView: View,
-    resources: Resources
+
+open class StatusBarSystemEventDefaultAnimator @JvmOverloads constructor(
+        resources: Resources,
+        private val onAlphaChanged: (Float) -> Unit,
+        private val onTranslationXChanged: (Float) -> Unit,
+        var isAnimationRunning: Boolean = false
 ) : SystemStatusAnimationCallback {
     private val translationXIn: Int = resources.getDimensionPixelSize(
             R.dimen.ongoing_appops_chip_animation_in_status_bar_translation_x)
@@ -46,18 +66,19 @@
             R.dimen.ongoing_appops_chip_animation_out_status_bar_translation_x)
 
     override fun onSystemEventAnimationBegin(): Animator {
+        isAnimationRunning = true
         val moveOut = ValueAnimator.ofFloat(0f, 1f).apply {
             duration = 23.frames
             interpolator = STATUS_BAR_X_MOVE_OUT
             addUpdateListener {
-                animatedView.translationX = -(translationXIn * animatedValue as Float)
+                onTranslationXChanged(-(translationXIn * animatedValue as Float))
             }
         }
         val alphaOut = ValueAnimator.ofFloat(1f, 0f).apply {
             duration = 8.frames
             interpolator = null
             addUpdateListener {
-                animatedView.alpha = animatedValue as Float
+                onAlphaChanged(animatedValue as Float)
             }
         }
 
@@ -67,13 +88,13 @@
     }
 
     override fun onSystemEventAnimationFinish(hasPersistentDot: Boolean): Animator {
-        animatedView.translationX = translationXOut.toFloat()
+        onTranslationXChanged(translationXOut.toFloat())
         val moveIn = ValueAnimator.ofFloat(1f, 0f).apply {
             duration = 23.frames
             startDelay = 7.frames
             interpolator = STATUS_BAR_X_MOVE_IN
             addUpdateListener {
-                animatedView.translationX = translationXOut * animatedValue as Float
+                onTranslationXChanged(translationXOut * animatedValue as Float)
             }
         }
         val alphaIn = ValueAnimator.ofFloat(0f, 1f).apply {
@@ -81,13 +102,14 @@
             startDelay = 11.frames
             interpolator = null
             addUpdateListener {
-                animatedView.alpha = animatedValue as Float
+                onAlphaChanged(animatedValue as Float)
             }
         }
 
         val animatorSet = AnimatorSet()
         animatorSet.playTogether(moveIn, alphaIn)
-
+        animatorSet.doOnEnd { isAnimationRunning = false }
+        animatorSet.doOnCancel { isAnimationRunning = false }
         return animatorSet
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt b/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt
index 030c54f..9952cfd 100644
--- a/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt
@@ -62,8 +62,6 @@
     BluetoothAdapter.OnMetadataChangedListener {
 
     private val stylusCallbacks: CopyOnWriteArrayList<StylusCallback> = CopyOnWriteArrayList()
-    private val stylusBatteryCallbacks: CopyOnWriteArrayList<StylusBatteryCallback> =
-        CopyOnWriteArrayList()
 
     // This map should only be accessed on the handler
     private val inputDeviceAddressMap: MutableMap<Int, String?> = ArrayMap()
@@ -106,14 +104,6 @@
         stylusCallbacks.remove(callback)
     }
 
-    fun registerBatteryCallback(callback: StylusBatteryCallback) {
-        stylusBatteryCallbacks.add(callback)
-    }
-
-    fun unregisterBatteryCallback(callback: StylusBatteryCallback) {
-        stylusBatteryCallbacks.remove(callback)
-    }
-
     override fun onInputDeviceAdded(deviceId: Int) {
         if (!hasStarted) return
 
@@ -195,7 +185,7 @@
                     "${device.address}: $isCharging"
             }
 
-            executeStylusBatteryCallbacks { cb ->
+            executeStylusCallbacks { cb ->
                 cb.onStylusBluetoothChargingStateChanged(inputDeviceId, device, isCharging)
             }
         }
@@ -221,7 +211,7 @@
                 onStylusUsed()
             }
 
-            executeStylusBatteryCallbacks { cb ->
+            executeStylusCallbacks { cb ->
                 cb.onStylusUsiBatteryStateChanged(deviceId, eventTimeMillis, batteryState)
             }
         }
@@ -329,10 +319,6 @@
         stylusCallbacks.forEach(run)
     }
 
-    private fun executeStylusBatteryCallbacks(run: (cb: StylusBatteryCallback) -> Unit) {
-        stylusBatteryCallbacks.forEach(run)
-    }
-
     private fun registerBatteryListener(deviceId: Int) {
         try {
             inputManager.addInputDeviceBatteryListener(deviceId, executor, this)
@@ -378,13 +364,6 @@
         fun onStylusBluetoothConnected(deviceId: Int, btAddress: String) {}
         fun onStylusBluetoothDisconnected(deviceId: Int, btAddress: String) {}
         fun onStylusFirstUsed() {}
-    }
-
-    /**
-     * Callback interface to receive stylus battery events from the StylusManager. All callbacks are
-     * runs on the same background handler.
-     */
-    interface StylusBatteryCallback {
         fun onStylusBluetoothChargingStateChanged(
             inputDeviceId: Int,
             btDevice: BluetoothDevice,
diff --git a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerStartable.kt b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerStartable.kt
index 27cafb1..3667392 100644
--- a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerStartable.kt
@@ -37,7 +37,7 @@
     private val inputManager: InputManager,
     private val stylusUsiPowerUi: StylusUsiPowerUI,
     private val featureFlags: FeatureFlags,
-) : CoreStartable, StylusManager.StylusCallback, StylusManager.StylusBatteryCallback {
+) : CoreStartable, StylusManager.StylusCallback {
 
     override fun onStylusAdded(deviceId: Int) {
         // On some devices, the addition of a new internal stylus indicates the use of a
@@ -74,7 +74,6 @@
 
         stylusUsiPowerUi.init()
         stylusManager.registerCallback(this)
-        stylusManager.registerBatteryCallback(this)
         stylusManager.startListener()
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbAccessoryUriActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbAccessoryUriActivity.java
index d5d3efd..3a7ac9c 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbAccessoryUriActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbAccessoryUriActivity.java
@@ -31,6 +31,9 @@
 import com.android.internal.app.AlertActivity;
 import com.android.internal.app.AlertController;
 import com.android.systemui.R;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+
+import javax.inject.Inject;
 
 /**
  * If the attached USB accessory has a URL associated with it, and that URL is valid,
@@ -46,13 +49,27 @@
     private UsbAccessory mAccessory;
     private Uri mUri;
 
+    private final DeviceProvisionedController mDeviceProvisionedController;
+
+    @Inject
+    UsbAccessoryUriActivity(DeviceProvisionedController deviceProvisionedController) {
+        mDeviceProvisionedController = deviceProvisionedController;
+    }
+
     @Override
     public void onCreate(Bundle icicle) {
-       getWindow().addSystemFlags(
+        getWindow().addSystemFlags(
                 WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
-       super.onCreate(icicle);
+        super.onCreate(icicle);
 
-       Intent intent = getIntent();
+        // Don't show this dialog during Setup Wizard
+        if (!mDeviceProvisionedController.isDeviceProvisioned()) {
+            Log.e(TAG, "device not provisioned");
+            finish();
+            return;
+        }
+
+        Intent intent = getIntent();
         mAccessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
         String uriString = intent.getStringExtra("uri");
         mUri = (uriString == null ? null : Uri.parse(uriString));
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index bd77c32..86ba30c 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -2210,6 +2210,32 @@
     }
 
     @Test
+    public void testPostureChangeToUnsupported_stopsFaceListeningState() {
+        // GIVEN device is listening for face
+        mKeyguardUpdateMonitor.mConfigFaceAuthSupportedPosture = DEVICE_POSTURE_CLOSED;
+        deviceInPostureStateClosed();
+        mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
+        mTestableLooper.processAllMessages();
+        keyguardIsVisible();
+
+        verifyFaceAuthenticateCall();
+
+        final CancellationSignal faceCancel = spy(mKeyguardUpdateMonitor.mFaceCancelSignal);
+        mKeyguardUpdateMonitor.mFaceCancelSignal = faceCancel;
+        KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class);
+        mKeyguardUpdateMonitor.registerCallback(callback);
+
+        // WHEN device is opened
+        deviceInPostureStateOpened();
+        mTestableLooper.processAllMessages();
+
+        // THEN face listening is stopped.
+        verify(faceCancel).cancel();
+        verify(callback).onBiometricRunningStateChanged(
+                eq(false), eq(BiometricSourceType.FACE));
+    }
+
+    @Test
     public void testShouldListenForFace_withLockedDown_returnsFalse()
             throws RemoteException {
         keyguardNotGoingAway();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java
index 47c9191..52a70ee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java
@@ -201,6 +201,13 @@
         assertThat(magnifierMediumButton.isSelected()).isTrue();
     }
 
+    @Test
+    public void showSettingPanel_focusOnThePanel() {
+        mWindowMagnificationSettings.showSettingPanel();
+
+        assertThat(mSettingView.isFocused()).isTrue();
+    }
+
     private <T extends View> T getInternalView(@IdRes int idRes) {
         T view = mSettingView.findViewById(idRes);
         assertNotNull(view);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
index 3a168d4..d6dbd73 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
@@ -450,6 +450,15 @@
         swipeToPosition(0f, Direction.DOWN, 0);
     }
 
+    @Test
+    public void testTouchSessionOnRemovedCalledTwice() {
+        mTouchHandler.onSessionStart(mTouchSession);
+        ArgumentCaptor<DreamTouchHandler.TouchSession.Callback> onRemovedCallbackCaptor =
+                ArgumentCaptor.forClass(DreamTouchHandler.TouchSession.Callback.class);
+        verify(mTouchSession).registerCallback(onRemovedCallbackCaptor.capture());
+        onRemovedCallbackCaptor.getValue().onRemoved();
+        onRemovedCallbackCaptor.getValue().onRemoved();
+    }
 
     private void swipeToPosition(float percent, Direction direction, float velocityY) {
         Mockito.clearInvocations(mTouchSession);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
index a4e5bca..984f4be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
@@ -45,6 +45,7 @@
 import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
+import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger
 import com.android.systemui.keyguard.ui.preview.KeyguardPreviewRenderer
 import com.android.systemui.keyguard.ui.preview.KeyguardPreviewRendererFactory
 import com.android.systemui.keyguard.ui.preview.KeyguardRemotePreviewManager
@@ -91,6 +92,7 @@
     @Mock private lateinit var launchAnimator: DialogLaunchAnimator
     @Mock private lateinit var commandQueue: CommandQueue
     @Mock private lateinit var devicePolicyManager: DevicePolicyManager
+    @Mock private lateinit var logger: KeyguardQuickAffordancesMetricsLogger
 
     private lateinit var underTest: CustomizationProvider
     private lateinit var testScope: TestScope
@@ -184,6 +186,7 @@
                 featureFlags = featureFlags,
                 repository = { quickAffordanceRepository },
                 launchAnimator = launchAnimator,
+                logger = logger,
                 devicePolicyManager = devicePolicyManager,
                 backgroundDispatcher = testDispatcher,
             )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
index 0469e77..0e6f8d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
@@ -219,6 +219,29 @@
         }
 
     @Test
+    fun isKeyguardUnlocked() =
+        runTest(UnconfinedTestDispatcher()) {
+            whenever(keyguardStateController.isUnlocked).thenReturn(false)
+            var latest: Boolean? = null
+            val job = underTest.isKeyguardUnlocked.onEach { latest = it }.launchIn(this)
+
+            assertThat(latest).isFalse()
+
+            val captor = argumentCaptor<KeyguardStateController.Callback>()
+            verify(keyguardStateController).addCallback(captor.capture())
+
+            whenever(keyguardStateController.isUnlocked).thenReturn(true)
+            captor.value.onUnlockedChanged()
+            assertThat(latest).isTrue()
+
+            whenever(keyguardStateController.isUnlocked).thenReturn(false)
+            captor.value.onUnlockedChanged()
+            assertThat(latest).isFalse()
+
+            job.cancel()
+        }
+
+    @Test
     fun isDozing() =
         runTest(UnconfinedTestDispatcher()) {
             var latest: Boolean? = null
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
index 84ec125..46c623a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
@@ -42,6 +42,7 @@
 import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
 import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceRegistry
 import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
+import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.settings.FakeUserTracker
 import com.android.systemui.settings.UserFileManager
@@ -225,6 +226,7 @@
     @Mock private lateinit var launchAnimator: DialogLaunchAnimator
     @Mock private lateinit var commandQueue: CommandQueue
     @Mock private lateinit var devicePolicyManager: DevicePolicyManager
+    @Mock private lateinit var logger: KeyguardQuickAffordancesMetricsLogger
 
     private lateinit var underTest: KeyguardQuickAffordanceInteractor
     private lateinit var testScope: TestScope
@@ -331,6 +333,7 @@
                 featureFlags = featureFlags,
                 repository = { quickAffordanceRepository },
                 launchAnimator = launchAnimator,
+                logger = logger,
                 devicePolicyManager = devicePolicyManager,
                 backgroundDispatcher = testDispatcher,
             )
@@ -360,10 +363,11 @@
                     KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
                 }
 
-            underTest.onQuickAffordanceTriggered(
-                configKey = BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS,
-                expandable = expandable,
-            )
+        underTest.onQuickAffordanceTriggered(
+            configKey = BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS,
+            expandable = expandable,
+            slotId = "",
+        )
 
             if (startActivity) {
                 if (needsToUnlockFirst) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
index 62c9e5f..cd579db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
@@ -45,6 +45,7 @@
 import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePickerRepresentation
 import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
 import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
+import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.settings.UserFileManager
 import com.android.systemui.settings.UserTracker
@@ -80,6 +81,7 @@
     @Mock private lateinit var launchAnimator: DialogLaunchAnimator
     @Mock private lateinit var commandQueue: CommandQueue
     @Mock private lateinit var devicePolicyManager: DevicePolicyManager
+    @Mock private lateinit var logger: KeyguardQuickAffordancesMetricsLogger
 
     private lateinit var underTest: KeyguardQuickAffordanceInteractor
 
@@ -186,6 +188,7 @@
                 featureFlags = featureFlags,
                 repository = { quickAffordanceRepository },
                 launchAnimator = launchAnimator,
+                logger = logger,
                 devicePolicyManager = devicePolicyManager,
                 backgroundDispatcher = testDispatcher,
             )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index ae7a928..2209852 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -19,6 +19,8 @@
 import android.animation.ValueAnimator
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardSecurityModel
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.Interpolators
 import com.android.systemui.flags.FakeFeatureFlags
@@ -40,6 +42,7 @@
 import com.android.systemui.shade.data.repository.FakeShadeRepository
 import com.android.systemui.shade.data.repository.ShadeRepository
 import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.mockito.withArgCaptor
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.cancelChildren
@@ -51,6 +54,8 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.Mock
 import org.mockito.Mockito.reset
 import org.mockito.Mockito.verify
@@ -77,6 +82,7 @@
     // Used to verify transition requests for test output
     @Mock private lateinit var mockTransitionRepository: KeyguardTransitionRepository
     @Mock private lateinit var commandQueue: CommandQueue
+    @Mock private lateinit var keyguardSecurityModel: KeyguardSecurityModel
 
     private lateinit var fromLockscreenTransitionInteractor: FromLockscreenTransitionInteractor
     private lateinit var fromDreamingTransitionInteractor: FromDreamingTransitionInteractor
@@ -102,6 +108,8 @@
         transitionRepository = KeyguardTransitionRepositoryImpl()
         runner = KeyguardTransitionRunner(transitionRepository)
 
+        whenever(keyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(PIN)
+
         val featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, true) }
         fromLockscreenTransitionInteractor =
             FromLockscreenTransitionInteractor(
@@ -173,16 +181,17 @@
                 keyguardInteractor = createKeyguardInteractor(featureFlags),
                 keyguardTransitionRepository = mockTransitionRepository,
                 keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
+                keyguardSecurityModel = keyguardSecurityModel,
             )
         fromPrimaryBouncerTransitionInteractor.start()
     }
 
     @Test
-    fun `DREAMING to LOCKSCREEN - dreaming state changes first`() =
+    fun `DREAMING to LOCKSCREEN`() =
         testScope.runTest {
-            // GIVEN a device is dreaming and occluded
+            // GIVEN a device is dreaming
             keyguardRepository.setDreamingWithOverlay(true)
-            keyguardRepository.setKeyguardOccluded(true)
+            keyguardRepository.setWakefulnessModel(startingToWake())
             runCurrent()
 
             // GIVEN a prior transition has run to DREAMING
@@ -215,56 +224,7 @@
 
             val info =
                 withArgCaptor<TransitionInfo> {
-                    verify(mockTransitionRepository).startTransition(capture())
-                }
-            // THEN a transition to BOUNCER should occur
-            assertThat(info.ownerName).isEqualTo("FromDreamingTransitionInteractor")
-            assertThat(info.from).isEqualTo(KeyguardState.DREAMING)
-            assertThat(info.to).isEqualTo(KeyguardState.LOCKSCREEN)
-            assertThat(info.animator).isNotNull()
-
-            coroutineContext.cancelChildren()
-        }
-
-    @Test
-    fun `DREAMING to LOCKSCREEN - occluded state changes first`() =
-        testScope.runTest {
-            // GIVEN a device is dreaming and occluded
-            keyguardRepository.setDreamingWithOverlay(true)
-            keyguardRepository.setKeyguardOccluded(true)
-            runCurrent()
-
-            // GIVEN a prior transition has run to DREAMING
-            runner.startTransition(
-                testScope,
-                TransitionInfo(
-                    ownerName = "",
-                    from = KeyguardState.LOCKSCREEN,
-                    to = KeyguardState.DREAMING,
-                    animator =
-                        ValueAnimator().apply {
-                            duration = 10
-                            interpolator = Interpolators.LINEAR
-                        },
-                )
-            )
-            runCurrent()
-            reset(mockTransitionRepository)
-
-            // WHEN doze is complete
-            keyguardRepository.setDozeTransitionModel(
-                DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
-            )
-            // AND occluded has stopped
-            keyguardRepository.setKeyguardOccluded(false)
-            advanceUntilIdle()
-            // AND then dreaming has stopped
-            keyguardRepository.setDreamingWithOverlay(false)
-            advanceUntilIdle()
-
-            val info =
-                withArgCaptor<TransitionInfo> {
-                    verify(mockTransitionRepository).startTransition(capture())
+                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
                 }
             // THEN a transition to BOUNCER should occur
             assertThat(info.ownerName).isEqualTo("FromDreamingTransitionInteractor")
@@ -304,7 +264,7 @@
 
             val info =
                 withArgCaptor<TransitionInfo> {
-                    verify(mockTransitionRepository).startTransition(capture())
+                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
                 }
             // THEN a transition to PRIMARY_BOUNCER should occur
             assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
@@ -345,7 +305,7 @@
 
             val info =
                 withArgCaptor<TransitionInfo> {
-                    verify(mockTransitionRepository).startTransition(capture())
+                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
                 }
             // THEN a transition to DOZING should occur
             assertThat(info.ownerName).isEqualTo("FromOccludedTransitionInteractor")
@@ -386,7 +346,7 @@
 
             val info =
                 withArgCaptor<TransitionInfo> {
-                    verify(mockTransitionRepository).startTransition(capture())
+                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
                 }
             // THEN a transition to DOZING should occur
             assertThat(info.ownerName).isEqualTo("FromOccludedTransitionInteractor")
@@ -398,6 +358,92 @@
         }
 
     @Test
+    fun `OCCLUDED to GONE`() =
+        testScope.runTest {
+            // GIVEN a device on lockscreen
+            keyguardRepository.setKeyguardShowing(true)
+            runCurrent()
+
+            // GIVEN a prior transition has run to OCCLUDED
+            runner.startTransition(
+                testScope,
+                TransitionInfo(
+                    ownerName = "",
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.OCCLUDED,
+                    animator =
+                        ValueAnimator().apply {
+                            duration = 10
+                            interpolator = Interpolators.LINEAR
+                        },
+                )
+            )
+            keyguardRepository.setKeyguardOccluded(true)
+            runCurrent()
+            reset(mockTransitionRepository)
+
+            // WHEN keyguard goes away
+            keyguardRepository.setKeyguardShowing(false)
+            // AND occlusion ends
+            keyguardRepository.setKeyguardOccluded(false)
+            runCurrent()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
+                }
+            // THEN a transition to GONE should occur
+            assertThat(info.ownerName).isEqualTo("FromOccludedTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.OCCLUDED)
+            assertThat(info.to).isEqualTo(KeyguardState.GONE)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
+    fun `OCCLUDED to LOCKSCREEN`() =
+        testScope.runTest {
+            // GIVEN a device on lockscreen
+            keyguardRepository.setKeyguardShowing(true)
+            runCurrent()
+
+            // GIVEN a prior transition has run to OCCLUDED
+            runner.startTransition(
+                testScope,
+                TransitionInfo(
+                    ownerName = "",
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.OCCLUDED,
+                    animator =
+                        ValueAnimator().apply {
+                            duration = 10
+                            interpolator = Interpolators.LINEAR
+                        },
+                )
+            )
+            keyguardRepository.setKeyguardOccluded(true)
+            runCurrent()
+            reset(mockTransitionRepository)
+
+            // WHEN occlusion ends
+            keyguardRepository.setKeyguardOccluded(false)
+            runCurrent()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
+                }
+            // THEN a transition to LOCKSCREEN should occur
+            assertThat(info.ownerName).isEqualTo("FromOccludedTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.OCCLUDED)
+            assertThat(info.to).isEqualTo(KeyguardState.LOCKSCREEN)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
     fun `LOCKSCREEN to DOZING`() =
         testScope.runTest {
             // GIVEN a device with AOD not available
@@ -427,7 +473,7 @@
 
             val info =
                 withArgCaptor<TransitionInfo> {
-                    verify(mockTransitionRepository).startTransition(capture())
+                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
                 }
             // THEN a transition to DOZING should occur
             assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
@@ -468,7 +514,7 @@
 
             val info =
                 withArgCaptor<TransitionInfo> {
-                    verify(mockTransitionRepository).startTransition(capture())
+                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
                 }
             // THEN a transition to DOZING should occur
             assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
@@ -505,7 +551,7 @@
 
             val info =
                 withArgCaptor<TransitionInfo> {
-                    verify(mockTransitionRepository).startTransition(capture())
+                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
                 }
             // THEN a transition to DOZING should occur
             assertThat(info.ownerName).isEqualTo("FromDozingTransitionInteractor")
@@ -542,7 +588,7 @@
 
             val info =
                 withArgCaptor<TransitionInfo> {
-                    verify(mockTransitionRepository).startTransition(capture())
+                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
                 }
             // THEN a transition to DOZING should occur
             assertThat(info.ownerName).isEqualTo("FromDozingTransitionInteractor")
@@ -583,7 +629,7 @@
 
             val info =
                 withArgCaptor<TransitionInfo> {
-                    verify(mockTransitionRepository).startTransition(capture())
+                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
                 }
             // THEN a transition to DOZING should occur
             assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
@@ -624,7 +670,7 @@
 
             val info =
                 withArgCaptor<TransitionInfo> {
-                    verify(mockTransitionRepository).startTransition(capture())
+                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
                 }
             // THEN a transition to AOD should occur
             assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
@@ -661,7 +707,7 @@
 
             val info =
                 withArgCaptor<TransitionInfo> {
-                    verify(mockTransitionRepository).startTransition(capture())
+                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
                 }
             // THEN a transition to AOD should occur
             assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
@@ -677,6 +723,7 @@
         testScope.runTest {
             // GIVEN a device that is not dreaming or dozing
             keyguardRepository.setDreamingWithOverlay(false)
+            keyguardRepository.setWakefulnessModel(startingToWake())
             keyguardRepository.setDozeTransitionModel(
                 DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
             )
@@ -704,7 +751,7 @@
 
             val info =
                 withArgCaptor<TransitionInfo> {
-                    verify(mockTransitionRepository).startTransition(capture())
+                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
                 }
             // THEN a transition to DREAMING should occur
             assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
@@ -741,7 +788,7 @@
 
             val info =
                 withArgCaptor<TransitionInfo> {
-                    verify(mockTransitionRepository).startTransition(capture())
+                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
                 }
             // THEN a transition to PRIMARY_BOUNCER should occur
             assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor")
@@ -784,7 +831,7 @@
 
             val info =
                 withArgCaptor<TransitionInfo> {
-                    verify(mockTransitionRepository).startTransition(capture())
+                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
                 }
             // THEN a transition to AOD should occur
             assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor")
@@ -828,7 +875,7 @@
 
             val info =
                 withArgCaptor<TransitionInfo> {
-                    verify(mockTransitionRepository).startTransition(capture())
+                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
                 }
             // THEN a transition to DOZING should occur
             assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor")
@@ -870,7 +917,7 @@
 
             val info =
                 withArgCaptor<TransitionInfo> {
-                    verify(mockTransitionRepository).startTransition(capture())
+                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
                 }
             // THEN a transition to LOCKSCREEN should occur
             assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor")
@@ -912,7 +959,7 @@
 
             val info =
                 withArgCaptor<TransitionInfo> {
-                    verify(mockTransitionRepository).startTransition(capture())
+                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
                 }
             // THEN a transition to AOD should occur
             assertThat(info.ownerName).isEqualTo("FromPrimaryBouncerTransitionInteractor")
@@ -954,7 +1001,7 @@
 
             val info =
                 withArgCaptor<TransitionInfo> {
-                    verify(mockTransitionRepository).startTransition(capture())
+                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
                 }
             // THEN a transition to DOZING should occur
             assertThat(info.ownerName).isEqualTo("FromPrimaryBouncerTransitionInteractor")
@@ -995,7 +1042,7 @@
 
             val info =
                 withArgCaptor<TransitionInfo> {
-                    verify(mockTransitionRepository).startTransition(capture())
+                    verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
                 }
             // THEN a transition to LOCKSCREEN should occur
             assertThat(info.ownerName).isEqualTo("FromPrimaryBouncerTransitionInteractor")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index c727b3a..bfc09d7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -45,6 +45,7 @@
 import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceRegistry
 import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
 import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
+import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.settings.UserFileManager
 import com.android.systemui.settings.UserTracker
@@ -89,6 +90,7 @@
     @Mock private lateinit var launchAnimator: DialogLaunchAnimator
     @Mock private lateinit var commandQueue: CommandQueue
     @Mock private lateinit var devicePolicyManager: DevicePolicyManager
+    @Mock private lateinit var logger: KeyguardQuickAffordancesMetricsLogger
 
     private lateinit var underTest: KeyguardBottomAreaViewModel
 
@@ -208,6 +210,7 @@
                         featureFlags = featureFlags,
                         repository = { quickAffordanceRepository },
                         launchAnimator = launchAnimator,
+                        logger = logger,
                         devicePolicyManager = devicePolicyManager,
                         backgroundDispatcher = testDispatcher,
                     ),
@@ -230,6 +233,7 @@
                     icon = mock(),
                     canShowWhileLocked = false,
                     intent = Intent("action"),
+                    slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(),
                 )
             val configKey =
                 setUpQuickAffordanceModel(
@@ -260,6 +264,7 @@
                     icon = mock(),
                     canShowWhileLocked = false,
                     intent = Intent("action"),
+                    slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(),
                 )
             val configKey =
                 setUpQuickAffordanceModel(
@@ -272,6 +277,7 @@
                 testConfig =
                     TestConfig(
                         isVisible = false,
+                        slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(),
                     ),
                 configKey = configKey,
             )
@@ -299,6 +305,7 @@
                             icon = icon,
                             canShowWhileLocked = false,
                             intent = Intent("action"),
+                            slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(),
                         ),
                 )
 
@@ -313,6 +320,7 @@
                         canShowWhileLocked = false,
                         intent = Intent("action"),
                         isSelected = true,
+                        slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(),
                     ),
                 configKey = configKey,
             )
@@ -341,6 +349,7 @@
                         icon = icon,
                         canShowWhileLocked = false,
                         intent = Intent("action"),
+                        slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(),
                     ),
             )
             val configKey =
@@ -354,6 +363,7 @@
                             icon = icon,
                             canShowWhileLocked = false,
                             intent = Intent("action"),
+                            slotId = KeyguardQuickAffordancePosition.BOTTOM_END.toSlotId(),
                         ),
                 )
 
@@ -368,6 +378,7 @@
                         canShowWhileLocked = false,
                         intent = Intent("action"),
                         isDimmed = true,
+                        slotId = KeyguardQuickAffordancePosition.BOTTOM_END.toSlotId(),
                     ),
                 configKey = configKey,
             )
@@ -387,6 +398,7 @@
                     canShowWhileLocked = false,
                     intent =
                         null, // This will cause it to tell the system that the click was handled.
+                    slotId = KeyguardQuickAffordancePosition.BOTTOM_END.toSlotId(),
                 )
             val configKey =
                 setUpQuickAffordanceModel(
@@ -409,6 +421,7 @@
             val config =
                 TestConfig(
                     isVisible = false,
+                    slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(),
                 )
             val configKey =
                 setUpQuickAffordanceModel(
@@ -434,6 +447,7 @@
                     icon = mock(),
                     canShowWhileLocked = false,
                     intent = Intent("action"),
+                    slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(),
                 )
 
             setUpQuickAffordanceModel(
@@ -513,6 +527,7 @@
                         isClickable = true,
                         icon = mock(),
                         canShowWhileLocked = true,
+                        slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(),
                     )
             )
             assertThat(value()).isTrue()
@@ -524,6 +539,7 @@
                         isClickable = true,
                         icon = mock(),
                         canShowWhileLocked = false,
+                        slotId = KeyguardQuickAffordancePosition.BOTTOM_END.toSlotId(),
                     )
             )
             assertThat(value()).isTrue()
@@ -532,6 +548,7 @@
                 testConfig =
                     TestConfig(
                         isVisible = false,
+                        slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(),
                     )
             )
             assertThat(value()).isTrue()
@@ -540,6 +557,7 @@
                 testConfig =
                     TestConfig(
                         isVisible = false,
+                        slotId = KeyguardQuickAffordancePosition.BOTTOM_END.toSlotId(),
                     )
             )
             assertThat(value()).isFalse()
@@ -594,6 +612,7 @@
                     icon = mock(),
                     canShowWhileLocked = false,
                     intent = Intent("action"),
+                    slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(),
                 )
             val configKey =
                 setUpQuickAffordanceModel(
@@ -626,6 +645,7 @@
                     icon = mock(),
                     canShowWhileLocked = false,
                     intent = Intent("action"),
+                    slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(),
                 )
             val configKey =
                 setUpQuickAffordanceModel(
@@ -656,6 +676,7 @@
                     icon = mock(),
                     canShowWhileLocked = false,
                     intent = Intent("action"),
+                    slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(),
                 )
             val configKey =
                 setUpQuickAffordanceModel(
@@ -684,6 +705,7 @@
                     icon = mock(),
                     canShowWhileLocked = false,
                     intent = Intent("action"),
+                    slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(),
                 )
             val configKey =
                 setUpQuickAffordanceModel(
@@ -748,12 +770,14 @@
         assertThat(viewModel.isActivated).isEqualTo(testConfig.isActivated)
         assertThat(viewModel.isSelected).isEqualTo(testConfig.isSelected)
         assertThat(viewModel.isDimmed).isEqualTo(testConfig.isDimmed)
+        assertThat(viewModel.slotId).isEqualTo(testConfig.slotId)
         if (testConfig.isVisible) {
             assertThat(viewModel.icon).isEqualTo(testConfig.icon)
             viewModel.onClicked.invoke(
                 KeyguardQuickAffordanceViewModel.OnClickedParameters(
                     configKey = configKey,
                     expandable = expandable,
+                    slotId = viewModel.slotId,
                 )
             )
             if (testConfig.intent != null) {
@@ -775,6 +799,7 @@
         val intent: Intent? = null,
         val isSelected: Boolean = false,
         val isDimmed: Boolean = false,
+        val slotId: String = ""
     ) {
         init {
             check(!isVisible || icon != null) { "Must supply non-null icon if visible!" }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
new file mode 100644
index 0000000..2a91799
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
@@ -0,0 +1,109 @@
+/*
+ * 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.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.statusbar.SysuiStatusBarStateController
+import com.android.systemui.util.mockito.whenever
+import com.google.common.collect.Range
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class PrimaryBouncerToGoneTransitionViewModelTest : SysuiTestCase() {
+    private lateinit var underTest: PrimaryBouncerToGoneTransitionViewModel
+    private lateinit var repository: FakeKeyguardTransitionRepository
+    @Mock private lateinit var statusBarStateController: SysuiStatusBarStateController
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        repository = FakeKeyguardTransitionRepository()
+        val interactor = KeyguardTransitionInteractor(repository)
+        underTest = PrimaryBouncerToGoneTransitionViewModel(interactor, statusBarStateController)
+    }
+
+    @Test
+    fun scrimBehindAlpha_leaveShadeOpen() =
+        runTest(UnconfinedTestDispatcher()) {
+            val values = mutableListOf<Float>()
+
+            val job = underTest.scrimBehindAlpha.onEach { values.add(it) }.launchIn(this)
+
+            whenever(statusBarStateController.leaveOpenOnKeyguardHide()).thenReturn(true)
+
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+            repository.sendTransitionStep(step(0.3f))
+            repository.sendTransitionStep(step(0.6f))
+            repository.sendTransitionStep(step(1f))
+
+            assertThat(values.size).isEqualTo(4)
+            values.forEach { assertThat(it).isEqualTo(1f) }
+
+            job.cancel()
+        }
+
+    @Test
+    fun scrimBehindAlpha_doNotLeaveShadeOpen() =
+        runTest(UnconfinedTestDispatcher()) {
+            val values = mutableListOf<Float>()
+
+            val job = underTest.scrimBehindAlpha.onEach { values.add(it) }.launchIn(this)
+
+            whenever(statusBarStateController.leaveOpenOnKeyguardHide()).thenReturn(false)
+
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+            repository.sendTransitionStep(step(0.3f))
+            repository.sendTransitionStep(step(0.6f))
+            repository.sendTransitionStep(step(1f))
+
+            assertThat(values.size).isEqualTo(4)
+            values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
+            assertThat(values[3]).isEqualTo(0f)
+
+            job.cancel()
+        }
+
+    private fun step(
+        value: Float,
+        state: TransitionState = TransitionState.RUNNING
+    ): TransitionStep {
+        return TransitionStep(
+            from = KeyguardState.PRIMARY_BOUNCER,
+            to = KeyguardState.GONE,
+            value = value,
+            transitionState = state,
+            ownerName = "PrimaryBouncerToGoneTransitionViewModelTest"
+        )
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java
index 515e1ee..3c08d58 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java
@@ -18,14 +18,14 @@
 
 import static android.app.Activity.RESULT_OK;
 
-import static com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_FOR_NOTE_ACCEPTED;
-import static com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_FOR_NOTE_CANCELLED;
+import static com.android.systemui.screenshot.appclips.AppClipsEvent.SCREENSHOT_FOR_NOTE_ACCEPTED;
+import static com.android.systemui.screenshot.appclips.AppClipsEvent.SCREENSHOT_FOR_NOTE_CANCELLED;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -34,36 +34,35 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.ApplicationInfoFlags;
 import android.graphics.Bitmap;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.ResultReceiver;
+import android.os.UserHandle;
 import android.testing.AndroidTestingRunner;
 import android.widget.ImageView;
 
-import androidx.lifecycle.MutableLiveData;
 import androidx.test.rule.ActivityTestRule;
 import androidx.test.runner.intercepting.SingleActivityFactory;
 
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.screenshot.AppClipsActivity;
-import com.android.systemui.screenshot.AppClipsTrampolineActivity;
-import com.android.systemui.screenshot.AppClipsViewModel;
+import com.android.systemui.screenshot.ImageExporter;
 import com.android.systemui.settings.UserTracker;
 
+import com.google.common.util.concurrent.Futures;
+
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.UUID;
+import java.util.concurrent.Executor;
 import java.util.function.BiConsumer;
 
 @RunWith(AndroidTestingRunner.class)
@@ -78,18 +77,16 @@
     private static final String TEST_CALLING_PACKAGE = "test-calling-package";
 
     @Mock
-    private AppClipsViewModel.Factory mViewModelFactory;
+    private AppClipsCrossProcessHelper mAppClipsCrossProcessHelper;
+    @Mock
+    private ImageExporter mImageExporter;
     @Mock
     private PackageManager mPackageManager;
     @Mock
     private UserTracker mUserTracker;
     @Mock
     private UiEventLogger mUiEventLogger;
-    @Mock
-    private AppClipsViewModel mViewModel;
 
-    private MutableLiveData<Bitmap> mScreenshotLiveData;
-    private MutableLiveData<Uri> mResultLiveData;
     private AppClipsActivity mActivity;
 
     // Using the deprecated ActivityTestRule and SingleActivityFactory to help with injecting mocks.
@@ -97,8 +94,11 @@
             new SingleActivityFactory<>(AppClipsActivityTestable.class) {
                 @Override
                 protected AppClipsActivityTestable create(Intent unUsed) {
-                    return new AppClipsActivityTestable(mViewModelFactory, mPackageManager,
-                            mUserTracker, mUiEventLogger);
+                    return new AppClipsActivityTestable(
+                            new AppClipsViewModel.Factory(mAppClipsCrossProcessHelper,
+                                    mImageExporter, getContext().getMainExecutor(),
+                                    directExecutor()), mPackageManager, mUserTracker,
+                            mUiEventLogger);
                 }
             };
 
@@ -110,29 +110,17 @@
     public void setUp() throws PackageManager.NameNotFoundException {
         MockitoAnnotations.initMocks(this);
 
-        mScreenshotLiveData = new MutableLiveData<>();
-        mResultLiveData = new MutableLiveData<>();
-        MutableLiveData<Integer> errorLiveData = new MutableLiveData<>();
-
-        when(mViewModelFactory.create(any(Class.class))).thenReturn(mViewModel);
-        when(mViewModel.getScreenshot()).thenReturn(mScreenshotLiveData);
-        when(mViewModel.getResultLiveData()).thenReturn(mResultLiveData);
-        when(mViewModel.getErrorLiveData()).thenReturn(errorLiveData);
         when(mUserTracker.getUserId()).thenReturn(TEST_USER_ID);
-
         ApplicationInfo applicationInfo = new ApplicationInfo();
         applicationInfo.uid = TEST_UID;
         when(mPackageManager.getApplicationInfoAsUser(eq(TEST_CALLING_PACKAGE),
                 any(ApplicationInfoFlags.class), eq(TEST_USER_ID))).thenReturn(applicationInfo);
 
-        doAnswer(invocation -> {
-            runOnMainThread(() -> mScreenshotLiveData.setValue(TEST_BITMAP));
-            return null;
-        }).when(mViewModel).performScreenshot();
-        doAnswer(invocation -> {
-            runOnMainThread(() -> mResultLiveData.setValue(TEST_URI));
-            return null;
-        }).when(mViewModel).saveScreenshotThenFinish(any(Drawable.class), any(Rect.class));
+        when(mAppClipsCrossProcessHelper.takeScreenshot()).thenReturn(TEST_BITMAP);
+        ImageExporter.Result result = new ImageExporter.Result();
+        result.uri = TEST_URI;
+        when(mImageExporter.export(any(Executor.class), any(UUID.class), any(Bitmap.class),
+                any(UserHandle.class))).thenReturn(Futures.immediateFuture(result));
     }
 
     @After
@@ -140,7 +128,6 @@
         mActivityRule.finishActivity();
     }
 
-    @Ignore("b/269403503")
     @Test
     public void appClipsLaunched_screenshotDisplayed() {
         launchActivity();
@@ -148,7 +135,6 @@
         assertThat(((ImageView) mActivity.findViewById(R.id.preview)).getDrawable()).isNotNull();
     }
 
-    @Ignore("b/269403503")
     @Test
     public void screenshotDisplayed_userConsented_screenshotExportedSuccessfully() {
         ResultReceiver resultReceiver = createResultReceiver((resultCode, data) -> {
@@ -168,7 +154,6 @@
         verify(mUiEventLogger).log(SCREENSHOT_FOR_NOTE_ACCEPTED, TEST_UID, TEST_CALLING_PACKAGE);
     }
 
-    @Ignore("b/269403503")
     @Test
     public void screenshotDisplayed_userDeclined() {
         ResultReceiver resultReceiver = createResultReceiver((resultCode, data) -> {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivityTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivityTest.java
index e40c49b..ad06dcc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivityTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivityTest.java
@@ -25,7 +25,8 @@
 import static android.content.Intent.EXTRA_CAPTURE_CONTENT_FOR_NOTE_STATUS_CODE;
 
 import static com.android.systemui.flags.Flags.SCREENSHOT_APP_CLIPS;
-import static com.android.systemui.screenshot.AppClipsTrampolineActivity.EXTRA_SCREENSHOT_URI;
+import static com.android.systemui.screenshot.appclips.AppClipsEvent.SCREENSHOT_FOR_NOTE_TRIGGERED;
+import static com.android.systemui.screenshot.appclips.AppClipsTrampolineActivity.EXTRA_SCREENSHOT_URI;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -59,8 +60,6 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.notetask.NoteTaskController;
-import com.android.systemui.screenshot.AppClipsTrampolineActivity;
-import com.android.systemui.screenshot.ScreenshotEvent;
 import com.android.systemui.settings.UserTracker;
 import com.android.wm.shell.bubbles.Bubbles;
 
@@ -262,8 +261,7 @@
         mActivityRule.launchActivity(mActivityIntent);
         waitForIdleSync();
 
-        verify(mUiEventLogger).log(ScreenshotEvent.SCREENSHOT_FOR_NOTE_TRIGGERED, TEST_UID,
-                TEST_CALLING_PACKAGE);
+        verify(mUiEventLogger).log(SCREENSHOT_FOR_NOTE_TRIGGERED, TEST_UID, TEST_CALLING_PACKAGE);
     }
 
     private void mockToSatisfyAllPrerequisites() throws NameNotFoundException {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsViewModelTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsViewModelTest.java
index d5af7ce1..e7c3c05 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsViewModelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsViewModelTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.appclips;
 
 import static android.content.Intent.CAPTURE_CONTENT_FOR_NOTE_FAILED;
 
@@ -36,7 +36,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.screenshot.appclips.AppClipsCrossProcessHelper;
+import com.android.systemui.screenshot.ImageExporter;
 
 import com.google.common.util.concurrent.Futures;
 
@@ -46,7 +46,6 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import java.time.ZonedDateTime;
 import java.util.UUID;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
@@ -62,7 +61,7 @@
     @Mock private AppClipsCrossProcessHelper mAppClipsCrossProcessHelper;
     @Mock private ImageExporter mImageExporter;
 
-    private com.android.systemui.screenshot.AppClipsViewModel mViewModel;
+    private AppClipsViewModel mViewModel;
 
     @Before
     public void setUp() {
@@ -99,8 +98,8 @@
 
     @Test
     public void saveScreenshot_throwsError_shouldUpdateErrorWithFailed() {
-        when(mImageExporter.export(any(Executor.class), any(UUID.class), eq(null), any(
-                ZonedDateTime.class), any(UserHandle.class))).thenReturn(
+        when(mImageExporter.export(any(Executor.class), any(UUID.class), eq(null),
+                any(UserHandle.class))).thenReturn(
                 Futures.immediateFailedFuture(new ExecutionException(new Throwable())));
 
         mViewModel.saveScreenshotThenFinish(FAKE_DRAWABLE, FAKE_RECT);
@@ -113,9 +112,9 @@
 
     @Test
     public void saveScreenshot_failsSilently_shouldUpdateErrorWithFailed() {
-        when(mImageExporter.export(any(Executor.class), any(UUID.class), eq(null), any(
-                ZonedDateTime.class), any(UserHandle.class))).thenReturn(
-                Futures.immediateFuture(new ImageExporter.Result()));
+        when(mImageExporter.export(any(Executor.class), any(UUID.class), eq(null),
+                any(UserHandle.class))).thenReturn(
+                        Futures.immediateFuture(new ImageExporter.Result()));
 
         mViewModel.saveScreenshotThenFinish(FAKE_DRAWABLE, FAKE_RECT);
         waitForIdleSync();
@@ -129,9 +128,8 @@
     public void saveScreenshot_succeeds_shouldUpdateResultWithUri() {
         ImageExporter.Result result = new ImageExporter.Result();
         result.uri = FAKE_URI;
-        when(mImageExporter.export(any(Executor.class), any(UUID.class), eq(null), any(
-                ZonedDateTime.class), any(UserHandle.class))).thenReturn(
-                Futures.immediateFuture(result));
+        when(mImageExporter.export(any(Executor.class), any(UUID.class), eq(null),
+                any(UserHandle.class))).thenReturn(Futures.immediateFuture(result));
 
         mViewModel.saveScreenshotThenFinish(FAKE_DRAWABLE, FAKE_RECT);
         waitForIdleSync();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 0a401b0..82a5743 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -33,6 +33,7 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel
+import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
 import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
 import com.android.systemui.statusbar.LockscreenShadeTransitionController
 import com.android.systemui.statusbar.NotificationInsetsController
@@ -65,48 +66,32 @@
 @RunWith(AndroidTestingRunner::class)
 @RunWithLooper(setAsMainLooper = true)
 class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
-    @Mock
-    private lateinit var view: NotificationShadeWindowView
-    @Mock
-    private lateinit var sysuiStatusBarStateController: SysuiStatusBarStateController
-    @Mock
-    private lateinit var centralSurfaces: CentralSurfaces
-    @Mock
-    private lateinit var dockManager: DockManager
-    @Mock
-    private lateinit var notificationPanelViewController: NotificationPanelViewController
-    @Mock
-    private lateinit var notificationShadeDepthController: NotificationShadeDepthController
-    @Mock
-    private lateinit var notificationShadeWindowController: NotificationShadeWindowController
-    @Mock
-    private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController
-    @Mock
-    private lateinit var ambientState: AmbientState
-    @Mock
-    private lateinit var keyguardBouncerViewModel: KeyguardBouncerViewModel
-    @Mock
-    private lateinit var stackScrollLayoutController: NotificationStackScrollLayoutController
-    @Mock
-    private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
-    @Mock
-    private lateinit var statusBarWindowStateController: StatusBarWindowStateController
+    @Mock private lateinit var view: NotificationShadeWindowView
+    @Mock private lateinit var sysuiStatusBarStateController: SysuiStatusBarStateController
+    @Mock private lateinit var centralSurfaces: CentralSurfaces
+    @Mock private lateinit var dockManager: DockManager
+    @Mock private lateinit var notificationPanelViewController: NotificationPanelViewController
+    @Mock private lateinit var notificationShadeDepthController: NotificationShadeDepthController
+    @Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
+    @Mock private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController
+    @Mock private lateinit var ambientState: AmbientState
+    @Mock private lateinit var keyguardBouncerViewModel: KeyguardBouncerViewModel
+    @Mock private lateinit var stackScrollLayoutController: NotificationStackScrollLayoutController
+    @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
+    @Mock private lateinit var statusBarWindowStateController: StatusBarWindowStateController
     @Mock
     private lateinit var lockscreenShadeTransitionController: LockscreenShadeTransitionController
-    @Mock
-    private lateinit var lockIconViewController: LockIconViewController
-    @Mock
-    private lateinit var phoneStatusBarViewController: PhoneStatusBarViewController
-    @Mock
-    private lateinit var pulsingGestureListener: PulsingGestureListener
-    @Mock
-    private lateinit var notificationInsetsController: NotificationInsetsController
-    @Mock
-    private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
+    @Mock private lateinit var lockIconViewController: LockIconViewController
+    @Mock private lateinit var phoneStatusBarViewController: PhoneStatusBarViewController
+    @Mock private lateinit var pulsingGestureListener: PulsingGestureListener
+    @Mock private lateinit var notificationInsetsController: NotificationInsetsController
+    @Mock private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
     @Mock lateinit var keyguardBouncerComponentFactory: KeyguardBouncerComponent.Factory
     @Mock lateinit var keyguardBouncerComponent: KeyguardBouncerComponent
     @Mock lateinit var keyguardSecurityContainerController: KeyguardSecurityContainerController
     @Mock lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
+    @Mock
+    lateinit var primaryBouncerToGoneTransitionViewModel: PrimaryBouncerToGoneTransitionViewModel
 
     private lateinit var interactionEventHandlerCaptor: ArgumentCaptor<InteractionEventHandler>
     private lateinit var interactionEventHandler: InteractionEventHandler
@@ -118,43 +103,44 @@
         MockitoAnnotations.initMocks(this)
         whenever(view.bottom).thenReturn(VIEW_BOTTOM)
         whenever(view.findViewById<ViewGroup>(R.id.keyguard_bouncer_container))
-                .thenReturn(mock(ViewGroup::class.java))
+            .thenReturn(mock(ViewGroup::class.java))
         whenever(keyguardBouncerComponentFactory.create(any(ViewGroup::class.java)))
-                .thenReturn(keyguardBouncerComponent)
+            .thenReturn(keyguardBouncerComponent)
         whenever(keyguardBouncerComponent.securityContainerController)
-                .thenReturn(keyguardSecurityContainerController)
+            .thenReturn(keyguardSecurityContainerController)
         whenever(keyguardTransitionInteractor.lockscreenToDreamingTransition)
-                .thenReturn(emptyFlow<TransitionStep>())
-        underTest = NotificationShadeWindowViewController(
-            lockscreenShadeTransitionController,
-            FalsingCollectorFake(),
-            sysuiStatusBarStateController,
-            dockManager,
-            notificationShadeDepthController,
-            view,
-            notificationPanelViewController,
-            ShadeExpansionStateManager(),
-            stackScrollLayoutController,
-            statusBarKeyguardViewManager,
-            statusBarWindowStateController,
-            lockIconViewController,
-            centralSurfaces,
-            notificationShadeWindowController,
-            keyguardUnlockAnimationController,
-            notificationInsetsController,
-            ambientState,
-            pulsingGestureListener,
-            keyguardBouncerViewModel,
-            keyguardBouncerComponentFactory,
-            alternateBouncerInteractor,
-            keyguardTransitionInteractor,
-        )
+            .thenReturn(emptyFlow<TransitionStep>())
+        underTest =
+            NotificationShadeWindowViewController(
+                lockscreenShadeTransitionController,
+                FalsingCollectorFake(),
+                sysuiStatusBarStateController,
+                dockManager,
+                notificationShadeDepthController,
+                view,
+                notificationPanelViewController,
+                ShadeExpansionStateManager(),
+                stackScrollLayoutController,
+                statusBarKeyguardViewManager,
+                statusBarWindowStateController,
+                lockIconViewController,
+                centralSurfaces,
+                notificationShadeWindowController,
+                keyguardUnlockAnimationController,
+                notificationInsetsController,
+                ambientState,
+                pulsingGestureListener,
+                keyguardBouncerViewModel,
+                keyguardBouncerComponentFactory,
+                alternateBouncerInteractor,
+                keyguardTransitionInteractor,
+                primaryBouncerToGoneTransitionViewModel,
+            )
         underTest.setupExpandedStatusBar()
 
-        interactionEventHandlerCaptor =
-            ArgumentCaptor.forClass(InteractionEventHandler::class.java)
+        interactionEventHandlerCaptor = ArgumentCaptor.forClass(InteractionEventHandler::class.java)
         verify(view).setInteractionEventHandler(interactionEventHandlerCaptor.capture())
-            interactionEventHandler = interactionEventHandlerCaptor.value
+        interactionEventHandler = interactionEventHandlerCaptor.value
     }
 
     // Note: So far, these tests only cover interactions with the status bar view controller. More
@@ -184,14 +170,11 @@
     @Test
     fun handleDispatchTouchEvent_downTouchBelowViewThenAnotherTouch_sendsTouchToSb() {
         underTest.setStatusBarViewController(phoneStatusBarViewController)
-        val downEvBelow = MotionEvent.obtain(
-            0L, 0L, MotionEvent.ACTION_DOWN, 0f, VIEW_BOTTOM + 4f, 0
-        )
+        val downEvBelow =
+            MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, VIEW_BOTTOM + 4f, 0)
         interactionEventHandler.handleDispatchTouchEvent(downEvBelow)
 
-        val nextEvent = MotionEvent.obtain(
-            0L, 0L, MotionEvent.ACTION_MOVE, 0f, VIEW_BOTTOM + 5f, 0
-        )
+        val nextEvent = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, VIEW_BOTTOM + 5f, 0)
         whenever(phoneStatusBarViewController.sendTouchToView(nextEvent)).thenReturn(true)
 
         val returnVal = interactionEventHandler.handleDispatchTouchEvent(nextEvent)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
index 5d71979..faa6221 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
@@ -46,6 +46,7 @@
 import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel;
+import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
 import com.android.systemui.statusbar.DragDownHelper;
 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
 import com.android.systemui.statusbar.NotificationInsetsController;
@@ -101,6 +102,7 @@
     @Mock private NotificationInsetsController mNotificationInsetsController;
     @Mock private AlternateBouncerInteractor mAlternateBouncerInteractor;
     @Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
+    @Mock private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel;
 
     @Captor private ArgumentCaptor<NotificationShadeWindowView.InteractionEventHandler>
             mInteractionEventHandlerCaptor;
@@ -150,7 +152,8 @@
                 mKeyguardBouncerViewModel,
                 mKeyguardBouncerComponentFactory,
                 mAlternateBouncerInteractor,
-                mKeyguardTransitionInteractor
+                mKeyguardTransitionInteractor,
+                mPrimaryBouncerToGoneTransitionViewModel
         );
         mController.setupExpandedStatusBar();
         mController.setDragDownHelper(mDragDownHelper);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index dc5a047..e1fba81 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -24,6 +24,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
+
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyFloat;
@@ -58,8 +60,14 @@
 import com.android.systemui.animation.ShadeInterpolation;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
 import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants;
+import com.android.systemui.keyguard.shared.model.KeyguardState;
+import com.android.systemui.keyguard.shared.model.TransitionState;
+import com.android.systemui.keyguard.shared.model.TransitionStep;
+import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
 import com.android.systemui.scrim.ScrimView;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.policy.FakeConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.concurrency.FakeExecutor;
@@ -85,8 +93,10 @@
 import java.util.HashSet;
 import java.util.Map;
 
+import kotlinx.coroutines.CoroutineDispatcher;
+
 @RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 public class ScrimControllerTest extends SysuiTestCase {
 
@@ -115,6 +125,11 @@
     @Mock private DockManager mDockManager;
     @Mock private ScreenOffAnimationController mScreenOffAnimationController;
     @Mock private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
+    @Mock private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel;
+    @Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
+    @Mock private CoroutineDispatcher mMainDispatcher;
+    @Mock private SysuiStatusBarStateController mSysuiStatusBarStateController;
+
     // TODO(b/204991468): Use a real PanelExpansionStateManager object once this bug is fixed. (The
     //   event-dispatch-on-registration pattern caused some of these unit tests to fail.)
     @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@@ -225,13 +240,22 @@
         when(mDelayedWakeLockBuilder.build()).thenReturn(mWakeLock);
         when(mDockManager.isDocked()).thenReturn(false);
 
+        when(mKeyguardTransitionInteractor.getPrimaryBouncerToGoneTransition())
+                .thenReturn(emptyFlow());
+        when(mPrimaryBouncerToGoneTransitionViewModel.getScrimBehindAlpha())
+                .thenReturn(emptyFlow());
+
         mScrimController = new ScrimController(mLightBarController,
                 mDozeParameters, mAlarmManager, mKeyguardStateController, mDelayedWakeLockBuilder,
                 new FakeHandler(mLooper.getLooper()), mKeyguardUpdateMonitor,
                 mDockManager, mConfigurationController, new FakeExecutor(new FakeSystemClock()),
                 mScreenOffAnimationController,
                 mKeyguardUnlockAnimationController,
-                mStatusBarKeyguardViewManager);
+                mStatusBarKeyguardViewManager,
+                mPrimaryBouncerToGoneTransitionViewModel,
+                mKeyguardTransitionInteractor,
+                mSysuiStatusBarStateController,
+                mMainDispatcher);
         mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
         mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront);
         mScrimController.setAnimatorListener(mAnimatorListener);
@@ -861,7 +885,11 @@
                 mDockManager, mConfigurationController, new FakeExecutor(new FakeSystemClock()),
                 mScreenOffAnimationController,
                 mKeyguardUnlockAnimationController,
-                mStatusBarKeyguardViewManager);
+                mStatusBarKeyguardViewManager,
+                mPrimaryBouncerToGoneTransitionViewModel,
+                mKeyguardTransitionInteractor,
+                mSysuiStatusBarStateController,
+                mMainDispatcher);
         mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
         mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront);
         mScrimController.setAnimatorListener(mAnimatorListener);
@@ -1629,6 +1657,28 @@
         assertScrimAlpha(mScrimBehind, 0);
     }
 
+    @Test
+    public void ignoreTransitionRequestWhileKeyguardTransitionRunning() {
+        mScrimController.transitionTo(ScrimState.UNLOCKED);
+        mScrimController.mPrimaryBouncerToGoneTransition.accept(
+                new TransitionStep(KeyguardState.PRIMARY_BOUNCER, KeyguardState.GONE, 0f,
+                        TransitionState.RUNNING, "ScrimControllerTest"));
+
+        // This request should not happen
+        mScrimController.transitionTo(ScrimState.BOUNCER);
+        assertThat(mScrimController.getState()).isEqualTo(ScrimState.UNLOCKED);
+    }
+
+    @Test
+    public void primaryBouncerToGoneOnFinishCallsKeyguardFadedAway() {
+        when(mKeyguardStateController.isKeyguardFadingAway()).thenReturn(true);
+        mScrimController.mPrimaryBouncerToGoneTransition.accept(
+                new TransitionStep(KeyguardState.PRIMARY_BOUNCER, KeyguardState.GONE, 0f,
+                        TransitionState.FINISHED, "ScrimControllerTest"));
+
+        verify(mStatusBarKeyguardViewManager).onKeyguardFadedAway();
+    }
+
     private void assertAlphaAfterExpansion(ScrimView scrim, float expectedAlpha, float expansion) {
         mScrimController.setRawPanelExpansionFraction(expansion);
         finishAnimationsImmediately();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusManagerTest.kt
index f8bf4b9..4525ad2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusManagerTest.kt
@@ -65,8 +65,6 @@
     @Mock lateinit var uiEventLogger: UiEventLogger
     @Mock lateinit var stylusCallback: StylusManager.StylusCallback
     @Mock lateinit var otherStylusCallback: StylusManager.StylusCallback
-    @Mock lateinit var stylusBatteryCallback: StylusManager.StylusBatteryCallback
-    @Mock lateinit var otherStylusBatteryCallback: StylusManager.StylusBatteryCallback
 
     private lateinit var mockitoSession: StaticMockitoSession
     private lateinit var stylusManager: StylusManager
@@ -123,7 +121,6 @@
 
         stylusManager.startListener()
         stylusManager.registerCallback(stylusCallback)
-        stylusManager.registerBatteryCallback(stylusBatteryCallback)
         clearInvocations(inputManager)
     }
 
@@ -434,23 +431,6 @@
     }
 
     @Test
-    fun onMetadataChanged_multipleRegisteredBatteryCallbacks_executesAll() {
-        stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID)
-        stylusManager.registerBatteryCallback(otherStylusBatteryCallback)
-
-        stylusManager.onMetadataChanged(
-            bluetoothDevice,
-            BluetoothDevice.METADATA_MAIN_CHARGING,
-            "true".toByteArray()
-        )
-
-        verify(stylusBatteryCallback, times(1))
-            .onStylusBluetoothChargingStateChanged(BT_STYLUS_DEVICE_ID, bluetoothDevice, true)
-        verify(otherStylusBatteryCallback, times(1))
-            .onStylusBluetoothChargingStateChanged(BT_STYLUS_DEVICE_ID, bluetoothDevice, true)
-    }
-
-    @Test
     fun onMetadataChanged_chargingStateTrue_executesBatteryCallbacks() {
         stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID)
 
@@ -460,7 +440,7 @@
             "true".toByteArray()
         )
 
-        verify(stylusBatteryCallback, times(1))
+        verify(stylusCallback, times(1))
             .onStylusBluetoothChargingStateChanged(BT_STYLUS_DEVICE_ID, bluetoothDevice, true)
     }
 
@@ -474,7 +454,7 @@
             "false".toByteArray()
         )
 
-        verify(stylusBatteryCallback, times(1))
+        verify(stylusCallback, times(1))
             .onStylusBluetoothChargingStateChanged(BT_STYLUS_DEVICE_ID, bluetoothDevice, false)
     }
 
@@ -486,7 +466,7 @@
             "true".toByteArray()
         )
 
-        verifyNoMoreInteractions(stylusBatteryCallback)
+        verifyNoMoreInteractions(stylusCallback)
     }
 
     @Test
@@ -499,8 +479,7 @@
             "true".toByteArray()
         )
 
-        verify(stylusBatteryCallback, never())
-            .onStylusBluetoothChargingStateChanged(any(), any(), any())
+        verify(stylusCallback, never()).onStylusBluetoothChargingStateChanged(any(), any(), any())
     }
 
     @Test
@@ -614,7 +593,7 @@
     fun onBatteryStateChanged_executesBatteryCallbacks() {
         stylusManager.onBatteryStateChanged(STYLUS_DEVICE_ID, 1, batteryState)
 
-        verify(stylusBatteryCallback, times(1))
+        verify(stylusCallback, times(1))
             .onStylusUsiBatteryStateChanged(STYLUS_DEVICE_ID, 1, batteryState)
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerStartableTest.kt
index 82b80f5..3db0ecc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerStartableTest.kt
@@ -96,7 +96,6 @@
         startable.start()
 
         verify(stylusManager, times(1)).registerCallback(startable)
-        verify(stylusManager, times(1)).registerBatteryCallback(startable)
     }
 
     @Test
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index 1a371c7..194ed02 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -47,6 +47,9 @@
     private val _isKeyguardShowing = MutableStateFlow(false)
     override val isKeyguardShowing: Flow<Boolean> = _isKeyguardShowing
 
+    private val _isKeyguardUnlocked = MutableStateFlow(false)
+    override val isKeyguardUnlocked: Flow<Boolean> = _isKeyguardUnlocked
+
     private val _isKeyguardOccluded = MutableStateFlow(false)
     override val isKeyguardOccluded: Flow<Boolean> = _isKeyguardOccluded
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
index eac1bd1..16442bb 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
@@ -37,7 +37,7 @@
         _transitions.emit(step)
     }
 
-    override fun startTransition(info: TransitionInfo): UUID? {
+    override fun startTransition(info: TransitionInfo, resetIfCanceled: Boolean): UUID? {
         return null
     }
 
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index a35cae9..542cc2f 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -59,6 +59,8 @@
 import android.companion.IAssociationRequestCallback;
 import android.companion.ICompanionDeviceManager;
 import android.companion.IOnAssociationsChangedListener;
+import android.companion.IOnMessageReceivedListener;
+import android.companion.IOnTransportsChangedListener;
 import android.companion.ISystemDataTransferCallback;
 import android.content.ComponentName;
 import android.content.Context;
@@ -232,7 +234,7 @@
                 /* cdmService */this, mAssociationStore);
         mCompanionAppController = new CompanionApplicationController(
                 context, mAssociationStore, mDevicePresenceMonitor);
-        mTransportManager = new CompanionTransportManager(context);
+        mTransportManager = new CompanionTransportManager(context, mAssociationStore);
         mSystemDataTransferProcessor = new SystemDataTransferProcessor(this, mAssociationStore,
                 mSystemDataTransferRequestStore, mTransportManager);
 
@@ -601,6 +603,37 @@
         }
 
         @Override
+        @GuardedBy("CompanionDeviceManagerService.this.mTransportManager.mTransports")
+        public void addOnTransportsChangedListener(IOnTransportsChangedListener listener) {
+            mTransportManager.addListener(listener);
+        }
+
+        @Override
+        @GuardedBy("CompanionDeviceManagerService.this.mTransportManager.mTransports")
+        public void removeOnTransportsChangedListener(IOnTransportsChangedListener listener) {
+            mTransportManager.removeListener(listener);
+        }
+
+        @Override
+        @GuardedBy("CompanionDeviceManagerService.this.mTransportManager.mTransports")
+        public void sendMessage(int messageType, byte[] data, int[] associationIds) {
+            mTransportManager.sendMessage(messageType, data, associationIds);
+        }
+
+        @Override
+        @GuardedBy("CompanionDeviceManagerService.this.mTransportManager.mTransports")
+        public void addOnMessageReceivedListener(int messageType,
+                IOnMessageReceivedListener listener) {
+            mTransportManager.addListener(messageType, listener);
+        }
+
+        @Override
+        public void removeOnMessageReceivedListener(int messageType,
+                IOnMessageReceivedListener listener) {
+            mTransportManager.removeListener(messageType, listener);
+        }
+
+        @Override
         public void legacyDisassociate(String deviceMacAddress, String packageName, int userId) {
             Log.i(TAG, "legacyDisassociate() pkg=u" + userId + "/" + packageName
                     + ", macAddress=" + deviceMacAddress);
diff --git a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
index 3fffdbe..f3a949d 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
@@ -31,6 +31,7 @@
 import android.app.PendingIntent;
 import android.companion.AssociationInfo;
 import android.companion.DeviceNotAssociatedException;
+import android.companion.IOnMessageReceivedListener;
 import android.companion.ISystemDataTransferCallback;
 import android.companion.datatransfer.PermissionSyncRequest;
 import android.companion.datatransfer.SystemDataTransferRequest;
@@ -40,6 +41,7 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.UserHandle;
@@ -92,8 +94,18 @@
         mAssociationStore = associationStore;
         mSystemDataTransferRequestStore = systemDataTransferRequestStore;
         mTransportManager = transportManager;
-        mTransportManager.addListener(MESSAGE_REQUEST_PERMISSION_RESTORE,
-                this::onReceivePermissionRestore);
+        IOnMessageReceivedListener messageListener = new IOnMessageReceivedListener() {
+            @Override
+            public void onMessageReceived(int associationId, byte[] data) throws RemoteException {
+                onReceivePermissionRestore(data);
+            }
+
+            @Override
+            public IBinder asBinder() {
+                return null;
+            }
+        };
+        mTransportManager.addListener(MESSAGE_REQUEST_PERMISSION_RESTORE, messageListener);
         mPermissionControllerManager = mContext.getSystemService(PermissionControllerManager.class);
         mExecutor = Executors.newSingleThreadExecutor();
     }
diff --git a/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java b/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java
index 8dab231..5390205 100644
--- a/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java
+++ b/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java
@@ -22,25 +22,32 @@
 import static com.android.server.companion.transport.Transport.MESSAGE_REQUEST_PLATFORM_INFO;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.app.ActivityManagerInternal;
+import android.companion.AssociationInfo;
+import android.companion.IOnMessageReceivedListener;
+import android.companion.IOnTransportsChangedListener;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.os.Binder;
 import android.os.Build;
+import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
 import android.util.Slog;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.server.LocalServices;
-import com.android.server.companion.transport.Transport.Listener;
+import com.android.server.companion.AssociationStore;
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.Future;
 
@@ -63,31 +70,100 @@
     }
 
     private final Context mContext;
+    private final AssociationStore mAssociationStore;
 
+    /** Association id -> Transport */
     @GuardedBy("mTransports")
     private final SparseArray<Transport> mTransports = new SparseArray<>();
-
     @NonNull
-    private final Map<Integer, Listener> mListeners = new HashMap<>();
+    private final RemoteCallbackList<IOnTransportsChangedListener> mTransportsListeners =
+            new RemoteCallbackList<>();
+    /** Message type -> IOnMessageReceivedListener */
+    @NonNull
+    private final SparseArray<IOnMessageReceivedListener> mMessageListeners = new SparseArray<>();
 
+
+    @Nullable
     private Transport mTempTransport;
 
-    public CompanionTransportManager(Context context) {
+    public CompanionTransportManager(Context context, AssociationStore associationStore) {
         mContext = context;
+        mAssociationStore = associationStore;
     }
 
     /**
-     * Add a message listener when a message is received for the message type
+     * Add a listener to receive callbacks when a message is received for the message type
      */
     @GuardedBy("mTransports")
-    public void addListener(int message, @NonNull Listener listener) {
-        mListeners.put(message, listener);
+    public void addListener(int message, @NonNull IOnMessageReceivedListener listener) {
+        mMessageListeners.put(message, listener);
         for (int i = 0; i < mTransports.size(); i++) {
             mTransports.valueAt(i).addListener(message, listener);
         }
     }
 
     /**
+     * Add a listener to receive callbacks when any of the transports is changed
+     */
+    @GuardedBy("mTransports")
+    public void addListener(IOnTransportsChangedListener listener) {
+        Slog.i(TAG, "Registering OnTransportsChangedListener");
+        mTransportsListeners.register(listener);
+        List<AssociationInfo> associations = new ArrayList<>();
+        for (int i = 0; i < mTransports.size(); i++) {
+            AssociationInfo association = mAssociationStore.getAssociationById(
+                    mTransports.keyAt(i));
+            if (association != null) {
+                associations.add(association);
+            }
+        }
+        mTransportsListeners.broadcast(listener1 -> {
+            // callback to the current listener with all the associations of the transports
+            // immediately
+            if (listener1 == listener) {
+                try {
+                    listener.onTransportsChanged(associations);
+                } catch (RemoteException ignored) {
+                }
+            }
+        });
+    }
+
+    /**
+     * Remove the listener for receiving callbacks when any of the transports is changed
+     */
+    public void removeListener(IOnTransportsChangedListener listener) {
+        mTransportsListeners.unregister(listener);
+    }
+
+    /**
+     * Remove the listener to stop receiving calbacks when a message is received for the given type
+     */
+    public void removeListener(int messageType, IOnMessageReceivedListener listener) {
+        mMessageListeners.remove(messageType);
+    }
+
+    /**
+     * Send a message to remote devices through the transports
+     */
+    @GuardedBy("mTransports")
+    public void sendMessage(int message, byte[] data, int[] associationIds) {
+        Slog.i(TAG, "Sending message 0x" + Integer.toHexString(message)
+                + " data length " + data.length);
+        for (int i = 0; i < associationIds.length; i++) {
+            if (mTransports.contains(associationIds[i])) {
+                try {
+                    mTransports.get(associationIds[i]).sendMessage(message, data);
+                } catch (IOException e) {
+                    Slog.e(TAG, "Failed to send message 0x" + Integer.toHexString(message)
+                            + " data length " + data.length + " to association "
+                            + associationIds[i]);
+                }
+            }
+        }
+    }
+
+    /**
      * For the moment, we only offer transporting of system data to built-in
      * companion apps; future work will improve the security model to support
      * third-party companion apps.
@@ -119,6 +195,8 @@
             }
 
             initializeTransport(associationId, fd);
+
+            notifyOnTransportsChanged();
         }
     }
 
@@ -130,16 +208,35 @@
                 mTransports.delete(associationId);
                 transport.stop();
             }
+
+            notifyOnTransportsChanged();
         }
     }
 
     @GuardedBy("mTransports")
+    private void notifyOnTransportsChanged() {
+        List<AssociationInfo> associations = new ArrayList<>();
+        for (int i = 0; i < mTransports.size(); i++) {
+            AssociationInfo association = mAssociationStore.getAssociationById(
+                    mTransports.keyAt(i));
+            if (association != null) {
+                associations.add(association);
+            }
+        }
+        mTransportsListeners.broadcast(listener -> {
+            try {
+                listener.onTransportsChanged(associations);
+            } catch (RemoteException ignored) {
+            }
+        });
+    }
+
+    @GuardedBy("mTransports")
     private void initializeTransport(int associationId, ParcelFileDescriptor fd) {
+        Slog.i(TAG, "Initializing transport");
         if (!isSecureTransportEnabled()) {
             Transport transport = new RawTransport(associationId, fd, mContext);
-            for (Map.Entry<Integer, Listener> entry : mListeners.entrySet()) {
-                transport.addListener(entry.getKey(), entry.getValue());
-            }
+            addMessageListenersToTransport(transport);
             transport.start();
             mTransports.put(associationId, transport);
             Slog.i(TAG, "RawTransport is created");
@@ -148,10 +245,21 @@
 
         // Exchange platform info to decide which transport should be created
         mTempTransport = new RawTransport(associationId, fd, mContext);
-        for (Map.Entry<Integer, Listener> entry : mListeners.entrySet()) {
-            mTempTransport.addListener(entry.getKey(), entry.getValue());
-        }
-        mTempTransport.addListener(MESSAGE_REQUEST_PLATFORM_INFO, this::onPlatformInfoReceived);
+        addMessageListenersToTransport(mTempTransport);
+        IOnMessageReceivedListener listener = new IOnMessageReceivedListener() {
+            @Override
+            public void onMessageReceived(int associationId, byte[] data) throws RemoteException {
+                synchronized (mTransports) {
+                    onPlatformInfoReceived(associationId, data);
+                }
+            }
+
+            @Override
+            public IBinder asBinder() {
+                return null;
+            }
+        };
+        mTempTransport.addListener(MESSAGE_REQUEST_PLATFORM_INFO, listener);
         mTempTransport.start();
 
         int sdk = Build.VERSION.SDK_INT;
@@ -163,14 +271,21 @@
                 .put(release.getBytes());
 
         // TODO: it should check if preSharedKey is given
-        mTempTransport.requestForResponse(MESSAGE_REQUEST_PLATFORM_INFO, data.array());
+        try {
+            mTempTransport.sendMessage(MESSAGE_REQUEST_PLATFORM_INFO, data.array());
+        } catch (IOException e) {
+            Slog.e(TAG, "Failed to exchange platform info");
+        }
     }
 
     /**
      * Depending on the remote platform info to decide which transport should be created
      */
-    @GuardedBy("mTransports")
-    private void onPlatformInfoReceived(byte[] data) {
+    @GuardedBy("CompanionTransportManager.this.mTransports")
+    private void onPlatformInfoReceived(int associationId, byte[] data) {
+        if (mTempTransport.getAssociationId() != associationId) {
+            return;
+        }
         // TODO: it should check if preSharedKey is given
 
         ByteBuffer buffer = ByteBuffer.wrap(data);
@@ -198,12 +313,11 @@
             Slog.i(TAG, "Creating a secure channel");
             transport = new SecureTransport(transport.getAssociationId(), transport.getFd(),
                     mContext);
-            for (Map.Entry<Integer, Listener> entry : mListeners.entrySet()) {
-                transport.addListener(entry.getKey(), entry.getValue());
-            }
+            addMessageListenersToTransport(transport);
             transport.start();
         }
         mTransports.put(transport.getAssociationId(), transport);
+        // Doesn't need to notifyTransportsChanged here, it'll be done in attachSystemDataTransport
     }
 
     public Future<?> requestPermissionRestore(int associationId, byte[] data) {
@@ -228,4 +342,10 @@
 
         return enabled;
     }
+
+    private void addMessageListenersToTransport(Transport transport) {
+        for (int i = 0; i < mMessageListeners.size(); i++) {
+            transport.addListener(mMessageListeners.keyAt(i), mMessageListeners.valueAt(i));
+        }
+    }
 }
diff --git a/services/companion/java/com/android/server/companion/transport/Transport.java b/services/companion/java/com/android/server/companion/transport/Transport.java
index e984c63..d69ce89 100644
--- a/services/companion/java/com/android/server/companion/transport/Transport.java
+++ b/services/companion/java/com/android/server/companion/transport/Transport.java
@@ -17,10 +17,12 @@
 package com.android.server.companion.transport;
 
 import android.annotation.NonNull;
+import android.companion.IOnMessageReceivedListener;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.Build;
 import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
 import android.util.Slog;
 import android.util.SparseArray;
 
@@ -45,7 +47,8 @@
     protected static final boolean DEBUG = Build.IS_DEBUGGABLE;
 
     static final int MESSAGE_REQUEST_PING = 0x63807378; // ?PIN
-    public static final int MESSAGE_REQUEST_PLATFORM_INFO = 0x63807086; // ?PFV
+    public static final int MESSAGE_REQUEST_PLATFORM_INFO = 0x63807073; // ?PFI
+    public static final int MESSAGE_REQUEST_CONTEXT_SYNC = 0x63678883; // ?CXS
     public static final int MESSAGE_REQUEST_PERMISSION_RESTORE = 0x63826983; // ?RES
 
     static final int MESSAGE_RESPONSE_SUCCESS = 0x33838567; // !SUC
@@ -59,19 +62,14 @@
     protected final OutputStream mRemoteOut;
     protected final Context mContext;
 
-    /** Message type -> Listener */
-    private final Map<Integer, Listener> mListeners;
-
     /**
-     * Message listener
+     * Message type -> Listener
+     *
+     * For now, the transport only supports 1 listener for each message type. If there's a need in
+     * the future to allow multiple listeners to receive callbacks for the same message type, the
+     * value of the map can be a list.
      */
-    public interface Listener {
-        /**
-         * Called when a message is received
-         * @param data data content in the message
-         */
-        void onDataReceived(byte[] data);
-    }
+    private final Map<Integer, IOnMessageReceivedListener> mListeners;
 
     private static boolean isRequest(int message) {
         return (message & 0xFF000000) == 0x63000000;
@@ -100,7 +98,7 @@
      * @param message Message type
      * @param listener Execute when a message with the type is received
      */
-    public void addListener(int message, Listener listener) {
+    public void addListener(int message, IOnMessageReceivedListener listener) {
         mListeners.put(message, listener);
     }
 
@@ -117,6 +115,13 @@
     protected abstract void sendMessage(int message, int sequence, @NonNull byte[] data)
             throws IOException;
 
+    /**
+     * Send a message
+     */
+    public void sendMessage(int message, @NonNull byte[] data) throws IOException {
+        sendMessage(message, mNextSequence.incrementAndGet(), data);
+    }
+
     public Future<byte[]> requestForResponse(int message, byte[] data) {
         if (DEBUG) Slog.d(TAG, "Requesting for response");
         final int sequence = mNextSequence.incrementAndGet();
@@ -165,7 +170,8 @@
                 sendMessage(MESSAGE_RESPONSE_SUCCESS, sequence, data);
                 break;
             }
-            case MESSAGE_REQUEST_PLATFORM_INFO: {
+            case MESSAGE_REQUEST_PLATFORM_INFO:
+            case MESSAGE_REQUEST_CONTEXT_SYNC: {
                 callback(message, data);
                 sendMessage(MESSAGE_RESPONSE_SUCCESS, sequence, EmptyArray.BYTE);
                 break;
@@ -196,7 +202,13 @@
 
     private void callback(int message, byte[] data) {
         if (mListeners.containsKey(message)) {
-            mListeners.get(message).onDataReceived(data);
+            try {
+                mListeners.get(message).onMessageReceived(getAssociationId(), data);
+                Slog.i(TAG, "Message 0x" + Integer.toHexString(message)
+                        + " is received from associationId " + mAssociationId
+                        + ", sending data length " + data.length + " to the listener.");
+            } catch (RemoteException ignored) {
+            }
         }
     }
 
diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
index 1c46028..34033e2 100644
--- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
+++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
@@ -134,7 +134,7 @@
     private final ArraySet<RunningAppsChangedListener> mRunningAppsChangedListeners =
             new ArraySet<>();
     @Nullable private final SecureWindowCallback mSecureWindowCallback;
-    @Nullable private final List<String> mDisplayCategories;
+    @Nullable private final Set<String> mDisplayCategories;
 
     private final boolean mShowTasksInHostDeviceRecents;
 
@@ -178,7 +178,7 @@
             @NonNull ActivityBlockedCallback activityBlockedCallback,
             @NonNull SecureWindowCallback secureWindowCallback,
             @NonNull IntentListenerCallback intentListenerCallback,
-            @NonNull List<String> displayCategories,
+            @NonNull Set<String> displayCategories,
             boolean showTasksInHostDeviceRecents) {
         super();
         mAllowedUsers = allowedUsers;
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 2d010cf..b338d89 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -97,6 +97,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
 import java.util.function.Consumer;
 
 
@@ -830,7 +831,7 @@
     }
 
     private GenericWindowPolicyController createWindowPolicyController(
-            @NonNull List<String> displayCategories) {
+            @NonNull Set<String> displayCategories) {
         final GenericWindowPolicyController gwpc =
                 new GenericWindowPolicyController(FLAG_SECURE,
                         SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS,
diff --git a/services/core/java/com/android/server/LogMteState.java b/services/core/java/com/android/server/LogMteState.java
new file mode 100644
index 0000000..410dd83
--- /dev/null
+++ b/services/core/java/com/android/server/LogMteState.java
@@ -0,0 +1,53 @@
+/*
+ * 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.server;
+
+import android.app.StatsManager;
+import android.content.Context;
+import android.util.StatsEvent;
+
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.os.Zygote;
+import com.android.internal.util.FrameworkStatsLog;
+
+import java.util.List;
+
+public class LogMteState {
+    public static void register(Context context) {
+        context.getSystemService(StatsManager.class)
+                .setPullAtomCallback(
+                        FrameworkStatsLog.MTE_STATE,
+                        null, // use default PullAtomMetadata values
+                        BackgroundThread.getExecutor(),
+                        new StatsManager.StatsPullAtomCallback() {
+                            @Override
+                            public int onPullAtom(int atomTag, List<StatsEvent> data) {
+                                if (atomTag != FrameworkStatsLog.MTE_STATE) {
+                                    throw new UnsupportedOperationException(
+                                            "Unknown tagId=" + atomTag);
+                                }
+                                data.add(
+                                        FrameworkStatsLog.buildStatsEvent(
+                                                FrameworkStatsLog.MTE_STATE,
+                                                Zygote.nativeSupportsMemoryTagging()
+                                                        ? FrameworkStatsLog.MTE_STATE__STATE__ON
+                                                        : FrameworkStatsLog.MTE_STATE__STATE__OFF));
+                                return StatsManager.PULL_SUCCESS;
+                            }
+                        });
+    }
+}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index f90a3ce..24c9e0f 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -2092,6 +2092,17 @@
                                 r.appInfo.uid, r.intent.getIntent(), r, r.userId,
                                 BackgroundStartPrivileges.NONE,
                                 false /* isBindService */);
+                        if (r.mAllowStartForeground == REASON_DENIED) {
+                            Slog.w(TAG_SERVICE, "FGS type change to/from SHORT_SERVICE: "
+                                    + " BFSL DENIED.");
+                        } else {
+                            if (DEBUG_SHORT_SERVICE) {
+                                Slog.w(TAG_SERVICE, "FGS type change to/from SHORT_SERVICE: "
+                                        + " BFSL Allowed: "
+                                        + PowerExemptionManager.reasonCodeToString(
+                                                r.mAllowStartForeground));
+                            }
+                        }
 
                         final boolean fgsStartAllowed =
                                 !isBgFgsRestrictionEnabledForService
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 432c209..1960c33 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -344,6 +344,7 @@
 import android.util.IndentingPrintWriter;
 import android.util.IntArray;
 import android.util.Log;
+import android.util.LogWriter;
 import android.util.Pair;
 import android.util.PrintWriterPrinter;
 import android.util.Slog;
@@ -484,9 +485,11 @@
 import java.util.UUID;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.function.BiFunction;
@@ -18865,10 +18868,11 @@
 
     @Override
     public void waitForBroadcastBarrier() {
-        waitForBroadcastBarrier(/* printWriter= */ null, false);
+        waitForBroadcastBarrier(/* printWriter= */ null, false, false);
     }
 
-    public void waitForBroadcastBarrier(@Nullable PrintWriter pw, boolean flushBroadcastLoopers) {
+    public void waitForBroadcastBarrier(@Nullable PrintWriter pw,
+            boolean flushBroadcastLoopers, boolean flushApplicationThreads) {
         enforceCallingPermission(permission.DUMP, "waitForBroadcastBarrier()");
         if (flushBroadcastLoopers) {
             BroadcastLoopers.waitForBarrier(pw);
@@ -18876,6 +18880,76 @@
         for (BroadcastQueue queue : mBroadcastQueues) {
             queue.waitForBarrier(pw);
         }
+        if (flushApplicationThreads) {
+            waitForApplicationBarrier(pw);
+        }
+    }
+
+    /**
+     * Wait for all pending {@link IApplicationThread} events to be processed in
+     * all currently running apps.
+     */
+    public void waitForApplicationBarrier(@Nullable PrintWriter pw) {
+        if (pw == null) {
+            pw = new PrintWriter(new LogWriter(Log.VERBOSE, TAG));
+        }
+
+        final CountDownLatch finishedLatch = new CountDownLatch(1);
+        final AtomicInteger pingCount = new AtomicInteger(0);
+        final AtomicInteger pongCount = new AtomicInteger(0);
+        final RemoteCallback pongCallback = new RemoteCallback((result) -> {
+            if (pongCount.incrementAndGet() == pingCount.get()) {
+                finishedLatch.countDown();
+            }
+        });
+
+        // Insert an extra "ping" as a sentinel value to guard us from finishing
+        // too quickly in parallel below
+        pingCount.incrementAndGet();
+
+        synchronized (mProcLock) {
+            final ArrayMap<String, SparseArray<ProcessRecord>> pmap =
+                    mProcessList.getProcessNamesLOSP().getMap();
+            final int numProc = pmap.size();
+            for (int iProc = 0; iProc < numProc; iProc++) {
+                final SparseArray<ProcessRecord> apps = pmap.valueAt(iProc);
+                for (int iApp = 0, numApps = apps.size(); iApp < numApps; iApp++) {
+                    final ProcessRecord app = apps.valueAt(iApp);
+                    final IApplicationThread thread = app.getOnewayThread();
+                    if (thread != null) {
+                        mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(app,
+                                OomAdjuster.OOM_ADJ_REASON_NONE);
+                        pingCount.incrementAndGet();
+                        try {
+                            thread.schedulePing(pongCallback);
+                        } catch (RemoteException ignored) {
+                            // When we failed to ping remote process, pretend as
+                            // if we received the expected pong
+                            pongCallback.sendResult(null);
+                        }
+                    }
+                }
+            }
+        }
+
+        // Now that we've dispatched all "ping" events above, we can send our
+        // "pong" sentinel value
+        pongCallback.sendResult(null);
+
+        // Wait for any remaining "pong" events to trickle in
+        for (int i = 0; i < 30; i++) {
+            try {
+                if (finishedLatch.await(1, TimeUnit.SECONDS)) {
+                    pw.println("Finished application barriers!");
+                    return;
+                } else {
+                    pw.println("Waiting for application barriers, at " + pongCount.get() + " of "
+                            + pingCount.get() + "...");
+                }
+            } catch (InterruptedException ignored) {
+            }
+        }
+        pw.println("Gave up waiting for application barriers!");
     }
 
     void setIgnoreDeliveryGroupPolicy(@NonNull String broadcastAction) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 02c1b8b..523ed69 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -359,6 +359,8 @@
                     return runWaitForBroadcastIdle(pw);
                 case "wait-for-broadcast-barrier":
                     return runWaitForBroadcastBarrier(pw);
+                case "wait-for-application-barrier":
+                    return runWaitForApplicationBarrier(pw);
                 case "set-ignore-delivery-group-policy":
                     return runSetIgnoreDeliveryGroupPolicy(pw);
                 case "clear-ignore-delivery-group-policy":
@@ -3332,16 +3334,24 @@
 
     int runWaitForBroadcastBarrier(PrintWriter pw) throws RemoteException {
         boolean flushBroadcastLoopers = false;
+        boolean flushApplicationThreads = false;
         String opt;
         while ((opt = getNextOption()) != null) {
             if (opt.equals("--flush-broadcast-loopers")) {
                 flushBroadcastLoopers = true;
+            } else if (opt.equals("--flush-application-threads")) {
+                flushApplicationThreads = true;
             } else {
                 getErrPrintWriter().println("Error: Unknown option: " + opt);
                 return -1;
             }
         }
-        mInternal.waitForBroadcastBarrier(pw, flushBroadcastLoopers);
+        mInternal.waitForBroadcastBarrier(pw, flushBroadcastLoopers, flushApplicationThreads);
+        return 0;
+    }
+
+    int runWaitForApplicationBarrier(PrintWriter pw) throws RemoteException {
+        mInternal.waitForApplicationBarrier(pw);
         return 0;
     }
 
diff --git a/services/core/java/com/android/server/am/AppExitInfoTracker.java b/services/core/java/com/android/server/am/AppExitInfoTracker.java
index 32d2071..1ba3266 100644
--- a/services/core/java/com/android/server/am/AppExitInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppExitInfoTracker.java
@@ -674,7 +674,7 @@
                         break;
                 }
             }
-        } catch (IOException | IllegalArgumentException | WireTypeMismatchException e) {
+        } catch (Exception e) {
             Slog.w(TAG, "Error in loading historical app exit info from persistent storage: " + e);
         } finally {
             if (fin != null) {
diff --git a/services/core/java/com/android/server/am/BroadcastHistory.java b/services/core/java/com/android/server/am/BroadcastHistory.java
index 6ac0e8b..34658ca 100644
--- a/services/core/java/com/android/server/am/BroadcastHistory.java
+++ b/services/core/java/com/android/server/am/BroadcastHistory.java
@@ -17,6 +17,7 @@
 package com.android.server.am;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Intent;
 import android.os.Bundle;
 import android.util.TimeUtils;
@@ -26,6 +27,7 @@
 
 import java.io.PrintWriter;
 import java.text.SimpleDateFormat;
+import java.util.ArrayList;
 import java.util.Date;
 
 /**
@@ -48,6 +50,11 @@
     }
 
     /**
+     * List of broadcasts which are being delivered or yet to be delivered.
+     */
+    private final ArrayList<BroadcastRecord> mPendingBroadcasts = new ArrayList<>();
+
+    /**
      * Historical data of past broadcasts, for debugging.  This is a ring buffer
      * whose last element is at mHistoryNext.
      */
@@ -70,7 +77,16 @@
     final long[] mSummaryHistoryDispatchTime;
     final long[] mSummaryHistoryFinishTime;
 
-    public void addBroadcastToHistoryLocked(BroadcastRecord original) {
+    void onBroadcastEnqueuedLocked(@NonNull BroadcastRecord r) {
+        mPendingBroadcasts.add(r);
+    }
+
+    void onBroadcastFinishedLocked(@NonNull BroadcastRecord r) {
+        mPendingBroadcasts.remove(r);
+        addBroadcastToHistoryLocked(r);
+    }
+
+    public void addBroadcastToHistoryLocked(@NonNull BroadcastRecord original) {
         // Note sometimes (only for sticky broadcasts?) we reuse BroadcastRecords,
         // So don't change the incoming record directly.
         final BroadcastRecord historyRecord = original.maybeStripForHistory();
@@ -93,7 +109,12 @@
     }
 
     @NeverCompile
-    public void dumpDebug(ProtoOutputStream proto) {
+    public void dumpDebug(@NonNull ProtoOutputStream proto) {
+        for (int i = 0; i < mPendingBroadcasts.size(); ++i) {
+            final BroadcastRecord r = mPendingBroadcasts.get(i);
+            r.dumpDebug(proto, BroadcastQueueProto.PENDING_BROADCASTS);
+        }
+
         int lastIndex = mHistoryNext;
         int ringIndex = lastIndex;
         do {
@@ -127,8 +148,20 @@
     }
 
     @NeverCompile
-    public boolean dumpLocked(PrintWriter pw, String dumpPackage, String queueName,
-            SimpleDateFormat sdf, boolean dumpAll, boolean needSep) {
+    public boolean dumpLocked(@NonNull PrintWriter pw, @Nullable String dumpPackage,
+            @NonNull String queueName, @NonNull SimpleDateFormat sdf,
+            boolean dumpAll, boolean needSep) {
+        pw.println("  Pending broadcasts:");
+        if (mPendingBroadcasts.isEmpty()) {
+            pw.println("    <empty>");
+        } else {
+            for (int idx = mPendingBroadcasts.size() - 1; idx >= 0; --idx) {
+                final BroadcastRecord r = mPendingBroadcasts.get(idx);
+                pw.print("  Broadcast #"); pw.print(idx); pw.println(":");
+                r.dump(pw, "    ", sdf);
+            }
+        }
+
         int i;
         boolean printed = false;
 
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index 84c03e5..32e5fd1 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -1145,8 +1145,11 @@
         pw.print(" because ");
         pw.print(reasonToString(mRunnableAtReason));
         pw.println();
-        pw.print("mProcessCached="); pw.println(mProcessCached);
+
         pw.increaseIndent();
+        dumpProcessState(pw);
+        dumpBroadcastCounts(pw);
+
         if (mActive != null) {
             dumpRecord("ACTIVE", now, pw, mActive, mActiveIndex);
         }
@@ -1167,6 +1170,49 @@
     }
 
     @NeverCompile
+    private void dumpProcessState(@NonNull IndentingPrintWriter pw) {
+        final StringBuilder sb = new StringBuilder();
+        if (mProcessCached) {
+            sb.append("CACHED");
+        }
+        if (mProcessInstrumented) {
+            if (sb.length() > 0) sb.append("|");
+            sb.append("INSTR");
+        }
+        if (mProcessPersistent) {
+            if (sb.length() > 0) sb.append("|");
+            sb.append("PER");
+        }
+        if (sb.length() > 0) {
+            pw.print("state:"); pw.println(sb);
+        }
+        if (runningOomAdjusted) {
+            pw.print("runningOomAdjusted:"); pw.println(runningOomAdjusted);
+        }
+    }
+
+    @NeverCompile
+    private void dumpBroadcastCounts(@NonNull IndentingPrintWriter pw) {
+        pw.print("e:"); pw.print(mCountEnqueued);
+        pw.print(" d:"); pw.print(mCountDeferred);
+        pw.print(" f:"); pw.print(mCountForeground);
+        pw.print(" fd:"); pw.print(mCountForegroundDeferred);
+        pw.print(" o:"); pw.print(mCountOrdered);
+        pw.print(" a:"); pw.print(mCountAlarm);
+        pw.print(" p:"); pw.print(mCountPrioritized);
+        pw.print(" pd:"); pw.print(mCountPrioritizedDeferred);
+        pw.print(" int:"); pw.print(mCountInteractive);
+        pw.print(" rt:"); pw.print(mCountResultTo);
+        pw.print(" ins:"); pw.print(mCountInstrumented);
+        pw.print(" m:"); pw.print(mCountManifest);
+
+        pw.print(" csi:"); pw.print(mActiveCountSinceIdle);
+        pw.print(" ccu:"); pw.print(mActiveCountConsecutiveUrgent);
+        pw.print(" ccn:"); pw.print(mActiveCountConsecutiveNormal);
+        pw.println();
+    }
+
+    @NeverCompile
     private void dumpRecord(@Nullable String flavor, @UptimeMillisLong long now,
             @NonNull IndentingPrintWriter pw, @NonNull BroadcastRecord record, int recordIndex) {
         TimeUtils.formatDuration(record.enqueueTime, now, pw);
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index 841b61e..81ca267 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -90,7 +90,6 @@
 import java.io.PrintWriter;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
@@ -602,6 +601,7 @@
         r.enqueueTime = SystemClock.uptimeMillis();
         r.enqueueRealTime = SystemClock.elapsedRealtime();
         r.enqueueClockTime = System.currentTimeMillis();
+        mHistory.onBroadcastEnqueuedLocked(r);
 
         ArraySet<BroadcastRecord> replacedBroadcasts = mReplacedBroadcastsCache.getAndSet(null);
         if (replacedBroadcasts == null) {
@@ -825,8 +825,9 @@
         if (app != null && app.isInFullBackup()) {
             return "isInFullBackup";
         }
-        if (mSkipPolicy.shouldSkip(r, receiver)) {
-            return "mSkipPolicy";
+        final String skipReason = mSkipPolicy.shouldSkipMessage(r, receiver);
+        if (skipReason != null) {
+            return skipReason;
         }
         final Intent receiverIntent = r.getReceiverIntent(receiver);
         if (receiverIntent == null) {
@@ -1100,7 +1101,8 @@
      */
     private void setDeliveryState(@Nullable BroadcastProcessQueue queue,
             @Nullable ProcessRecord app, @NonNull BroadcastRecord r, int index,
-            @NonNull Object receiver, @DeliveryState int newDeliveryState, String reason) {
+            @NonNull Object receiver, @DeliveryState int newDeliveryState,
+            @NonNull String reason) {
         final int cookie = traceBegin("setDeliveryState");
         final int oldDeliveryState = getDeliveryState(r, index);
         boolean checkFinished = false;
@@ -1108,7 +1110,7 @@
         // Only apply state when we haven't already reached a terminal state;
         // this is how we ignore racing timeout messages
         if (!isDeliveryStateTerminal(oldDeliveryState)) {
-            r.setDeliveryState(index, newDeliveryState);
+            r.setDeliveryState(index, newDeliveryState, reason);
             if (oldDeliveryState == BroadcastRecord.DELIVERY_DEFERRED) {
                 r.deferredCount--;
             } else if (newDeliveryState == BroadcastRecord.DELIVERY_DEFERRED) {
@@ -1659,7 +1661,7 @@
         mService.notifyBroadcastFinishedLocked(r);
         r.finishTime = SystemClock.uptimeMillis();
         r.nextReceiver = r.receivers.size();
-        mHistory.addBroadcastToHistoryLocked(r);
+        mHistory.onBroadcastFinishedLocked(r);
 
         BroadcastQueueImpl.logBootCompletedBroadcastCompletionLatencyIfPossible(r);
 
@@ -1833,8 +1835,9 @@
         if (dumpConstants) {
             mConstants.dump(ipw);
         }
+
         if (dumpHistory) {
-            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+            final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
             needSep = mHistory.dumpLocked(ipw, dumpPackage, mQueueName, sdf, dumpAll, needSep);
         }
         return needSep;
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index f793c50..59f33dd 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -99,6 +99,7 @@
     final @Nullable BroadcastOptions options; // BroadcastOptions supplied by caller
     final @NonNull List<Object> receivers;   // contains BroadcastFilter and ResolveInfo
     final @DeliveryState int[] delivery;   // delivery state of each receiver
+    final @NonNull String[] deliveryReasons; // reasons for delivery state of each receiver
     final boolean[] deferredUntilActive; // whether each receiver is infinitely deferred
     final int[] blockedUntilTerminalCount; // blocked until count of each receiver
     @Nullable ProcessRecord resultToApp; // who receives final result if non-null
@@ -298,7 +299,7 @@
                     pw.print(" initialSticky="); pw.println(initialSticky);
         }
         if (nextReceiver != 0) {
-            pw.print(prefix); pw.print("nextReceiver="); pw.print(nextReceiver);
+            pw.print(prefix); pw.print("nextReceiver="); pw.println(nextReceiver);
         }
         if (curFilter != null) {
             pw.print(prefix); pw.print("curFilter="); pw.println(curFilter);
@@ -328,6 +329,7 @@
             }
             pw.print(prefix); pw.print("state="); pw.print(state); pw.println(stateStr);
         }
+        pw.print(prefix); pw.print("terminalCount="); pw.println(terminalCount);
         final int N = receivers != null ? receivers.size() : 0;
         String p2 = prefix + "  ";
         PrintWriterPrinter printer = new PrintWriterPrinter(pw);
@@ -346,6 +348,7 @@
                 TimeUtils.formatDuration(terminalTime[i] - scheduledTime[i], pw);
                 pw.print(' ');
             }
+            pw.print("("); pw.print(blockedUntilTerminalCount[i]); pw.print(") ");
             pw.print("#"); pw.print(i); pw.print(": ");
             if (o instanceof BroadcastFilter) {
                 pw.println(o);
@@ -356,6 +359,9 @@
             } else {
                 pw.println(o);
             }
+            if (deliveryReasons[i] != null) {
+                pw.print(p2); pw.print("reason: "); pw.println(deliveryReasons[i]);
+            }
         }
     }
 
@@ -393,6 +399,7 @@
         options = _options;
         receivers = (_receivers != null) ? _receivers : EMPTY_RECEIVERS;
         delivery = new int[_receivers != null ? _receivers.size() : 0];
+        deliveryReasons = new String[delivery.length];
         deferUntilActive = options != null ? options.isDeferUntilActive() : false;
         deferredUntilActive = new boolean[deferUntilActive ? delivery.length : 0];
         blockedUntilTerminalCount = calculateBlockedUntilTerminalCount(receivers, _serialized);
@@ -448,6 +455,7 @@
         options = from.options;
         receivers = from.receivers;
         delivery = from.delivery;
+        deliveryReasons = from.deliveryReasons;
         deferUntilActive = from.deferUntilActive;
         deferredUntilActive = from.deferredUntilActive;
         blockedUntilTerminalCount = from.blockedUntilTerminalCount;
@@ -609,8 +617,10 @@
      * Update the delivery state of the given {@link #receivers} index.
      * Automatically updates any time measurements related to state changes.
      */
-    void setDeliveryState(int index, @DeliveryState int deliveryState) {
+    void setDeliveryState(int index, @DeliveryState int deliveryState,
+            @NonNull String reason) {
         delivery[index] = deliveryState;
+        deliveryReasons[index] = reason;
         if (deferUntilActive) deferredUntilActive[index] = false;
         switch (deliveryState) {
             case DELIVERY_DELIVERED:
@@ -977,7 +987,8 @@
             if (label == null) {
                 label = intent.toString();
             }
-            mCachedToShortString = label + "/u" + userId;
+            mCachedToShortString = Integer.toHexString(System.identityHashCode(this))
+                    + ":" + label + "/u" + userId;
         }
         return mCachedToShortString;
     }
diff --git a/services/core/java/com/android/server/am/SameProcessApplicationThread.java b/services/core/java/com/android/server/am/SameProcessApplicationThread.java
index 82dd5c2..6deaf7b 100644
--- a/services/core/java/com/android/server/am/SameProcessApplicationThread.java
+++ b/services/core/java/com/android/server/am/SameProcessApplicationThread.java
@@ -25,6 +25,7 @@
 import android.content.res.CompatibilityInfo;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.RemoteCallback;
 import android.os.RemoteException;
 
 import java.util.List;
@@ -77,17 +78,23 @@
 
     @Override
     public void scheduleReceiverList(List<ReceiverInfo> info) {
-        for (int i = 0; i < info.size(); i++) {
-            ReceiverInfo r = info.get(i);
-            if (r.registered) {
-                scheduleRegisteredReceiver(r.receiver, r.intent,
-                        r.resultCode, r.data, r.extras, r.ordered, r.sticky, r.assumeDelivered,
-                        r.sendingUser, r.processState, r.sendingUid, r.sendingPackage);
-            } else {
-                scheduleReceiver(r.intent, r.activityInfo, r.compatInfo,
-                        r.resultCode, r.data, r.extras, r.sync, r.assumeDelivered,
-                        r.sendingUser, r.processState, r.sendingUid, r.sendingPackage);
+        mHandler.post(() -> {
+            try {
+                mWrapped.scheduleReceiverList(info);
+            } catch (RemoteException e) {
+                throw new RuntimeException(e);
             }
-        }
+        });
+    }
+
+    @Override
+    public void schedulePing(RemoteCallback pong) {
+        mHandler.post(() -> {
+            try {
+                mWrapped.schedulePing(pong);
+            } catch (RemoteException e) {
+                throw new RuntimeException(e);
+            }
+        });
     }
 }
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index ae8ceab..6180117 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -468,6 +468,8 @@
             long fgToken = proto.start(ServiceRecordProto.FOREGROUND);
             proto.write(ServiceRecordProto.Foreground.ID, foregroundId);
             foregroundNoti.dumpDebug(proto, ServiceRecordProto.Foreground.NOTIFICATION);
+            proto.write(ServiceRecordProto.Foreground.FOREGROUND_SERVICE_TYPE,
+                    foregroundServiceType);
             proto.end(fgToken);
         }
         ProtoUtils.toDuration(proto, ServiceRecordProto.CREATE_REAL_TIME, createRealTime, nowReal);
diff --git a/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java b/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
index 2d6966a..bb8d3f4 100644
--- a/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
+++ b/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
@@ -213,8 +213,6 @@
 
     @Override
     public void updateUidProcState(int uid, int procState, int capability) {
-        mEventLog.logUpdateUidProcState(uid, procState, capability);
-
         int uidState = processStateToUidState(procState);
 
         int prevUidState = mUidStates.get(uid, AppOpsManager.MIN_PRIORITY_UID_STATE);
@@ -226,6 +224,10 @@
                 && (uidState != prevUidState || capability != prevCapability))
                 || (pendingStateCommitTime != 0
                 && (uidState != pendingUidState || capability != pendingCapability))) {
+
+            // If this process update results in a capability or uid state change, log it. It's
+            // not interesting otherwise.
+            mEventLog.logUpdateUidProcState(uid, procState, capability);
             mPendingUidStates.put(uid, uidState);
             mPendingCapability.put(uid, capability);
 
@@ -389,10 +391,8 @@
 
     private static class EventLog {
 
-        // These seems a bit too verbose and not as useful, turning off for now.
-        // DCE should be able to remove most associated code.
         // Memory usage: 16 * size bytes
-        private static final int UPDATE_UID_PROC_STATE_LOG_MAX_SIZE = 0;
+        private static final int UPDATE_UID_PROC_STATE_LOG_MAX_SIZE = 200;
         // Memory usage: 20 * size bytes
         private static final int COMMIT_UID_STATE_LOG_MAX_SIZE = 200;
         // Memory usage: 24 * size bytes
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 21cc172..55d2921 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2126,16 +2126,24 @@
                 autoHdrOutputTypes = getEnabledAutoHdrTypesLocked();
             }
 
+            int conversionMode = hdrConversionMode.getConversionMode();
+            int preferredHdrType = hdrConversionMode.getPreferredHdrOutputType();
             // If the HDR conversion is disabled by an app through WindowManager.LayoutParams, then
             // set HDR conversion mode to HDR_CONVERSION_PASSTHROUGH.
             if (mOverrideHdrConversionMode == null) {
-                mSystemPreferredHdrOutputType =
-                        mInjector.setHdrConversionMode(hdrConversionMode.getConversionMode(),
-                        hdrConversionMode.getPreferredHdrOutputType(), autoHdrOutputTypes);
+                // HDR_CONVERSION_FORCE with HDR_TYPE_INVALID is used to represent forcing SDR type.
+                // But, internally SDR is selected by using passthrough mode.
+                if (conversionMode == HdrConversionMode.HDR_CONVERSION_FORCE
+                        && preferredHdrType == Display.HdrCapabilities.HDR_TYPE_INVALID) {
+                    conversionMode = HdrConversionMode.HDR_CONVERSION_PASSTHROUGH;
+                }
             } else {
-                mInjector.setHdrConversionMode(mOverrideHdrConversionMode.getConversionMode(),
-                        mOverrideHdrConversionMode.getPreferredHdrOutputType(), null);
+                conversionMode = mOverrideHdrConversionMode.getConversionMode();
+                preferredHdrType = mOverrideHdrConversionMode.getPreferredHdrOutputType();
+                autoHdrOutputTypes = null;
             }
+            mSystemPreferredHdrOutputType = mInjector.setHdrConversionMode(
+                    conversionMode, preferredHdrType, autoHdrOutputTypes);
         }
     }
 
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index 364d53b..eda15ae 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -285,7 +285,7 @@
             mUniqueIndex = uniqueIndex;
             mIsDisplayOn = surface != null;
             mDisplayIdToMirror = virtualDisplayConfig.getDisplayIdToMirror();
-            mIsWindowManagerMirroring = virtualDisplayConfig.isWindowManagerMirroring();
+            mIsWindowManagerMirroring = virtualDisplayConfig.isWindowManagerMirroringEnabled();
         }
 
         @Override
diff --git a/services/core/java/com/android/server/input/KeyboardLayoutManager.java b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
index e0253fc..088740e 100644
--- a/services/core/java/com/android/server/input/KeyboardLayoutManager.java
+++ b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
@@ -150,6 +150,10 @@
     @Override
     public void onInputDeviceAdded(int deviceId) {
         onInputDeviceChanged(deviceId);
+        if (useNewSettingsUi()) {
+            // Force native callback to set up keyboard layout overlay for newly added keyboards
+            reloadKeyboardLayouts();
+        }
     }
 
     @Override
@@ -283,7 +287,8 @@
     public KeyboardLayout[] getKeyboardLayoutsForInputDevice(
             final InputDeviceIdentifier identifier) {
         if (useNewSettingsUi()) {
-            return new KeyboardLayout[0];
+            // Provide all supported keyboard layouts since Ime info is not provided
+            return getKeyboardLayouts();
         }
         final String[] enabledLayoutDescriptors =
                 getEnabledKeyboardLayoutsForInputDevice(identifier);
diff --git a/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java b/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
new file mode 100644
index 0000000..182aa6f
--- /dev/null
+++ b/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
@@ -0,0 +1,240 @@
+/*
+ * 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.server.media;
+
+import static android.media.MediaRoute2Info.FEATURE_LIVE_AUDIO;
+import static android.media.MediaRoute2Info.FEATURE_LIVE_VIDEO;
+import static android.media.MediaRoute2Info.FEATURE_LOCAL_PLAYBACK;
+import static android.media.MediaRoute2Info.TYPE_BUILTIN_SPEAKER;
+import static android.media.MediaRoute2Info.TYPE_DOCK;
+import static android.media.MediaRoute2Info.TYPE_HDMI;
+import static android.media.MediaRoute2Info.TYPE_USB_DEVICE;
+import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES;
+import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.media.AudioManager;
+import android.media.AudioRoutesInfo;
+import android.media.IAudioRoutesObserver;
+import android.media.IAudioService;
+import android.media.MediaRoute2Info;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Objects;
+
+
+/* package */ final class AudioPoliciesDeviceRouteController implements DeviceRouteController {
+
+    private static final String TAG = "APDeviceRoutesController";
+
+    private static final String DEVICE_ROUTE_ID = "DEVICE_ROUTE";
+
+    @NonNull
+    private final Context mContext;
+    @NonNull
+    private final AudioManager mAudioManager;
+    @NonNull
+    private final IAudioService mAudioService;
+
+    @NonNull
+    private final OnDeviceRouteChangedListener mOnDeviceRouteChangedListener;
+    @NonNull
+    private final AudioRoutesObserver mAudioRoutesObserver = new AudioRoutesObserver();
+
+    private int mDeviceVolume;
+
+    @NonNull
+    private MediaRoute2Info mDeviceRoute;
+    @Nullable
+    private MediaRoute2Info mSelectedRoute;
+
+    @VisibleForTesting
+    /* package */ AudioPoliciesDeviceRouteController(@NonNull Context context,
+            @NonNull AudioManager audioManager,
+            @NonNull IAudioService audioService,
+            @NonNull OnDeviceRouteChangedListener onDeviceRouteChangedListener) {
+        Objects.requireNonNull(context);
+        Objects.requireNonNull(audioManager);
+        Objects.requireNonNull(audioService);
+        Objects.requireNonNull(onDeviceRouteChangedListener);
+
+        mContext = context;
+        mOnDeviceRouteChangedListener = onDeviceRouteChangedListener;
+
+        mAudioManager = audioManager;
+        mAudioService = audioService;
+
+        AudioRoutesInfo newAudioRoutes = null;
+        try {
+            newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Cannot connect to audio service to start listen to routes", e);
+        }
+
+        mDeviceRoute = createRouteFromAudioInfo(newAudioRoutes);
+    }
+
+    @Override
+    public synchronized boolean selectRoute(@Nullable Integer type) {
+        if (type == null) {
+            mSelectedRoute = null;
+            return true;
+        }
+
+        if (!isDeviceRouteType(type)) {
+            return false;
+        }
+
+        mSelectedRoute = createRouteFromAudioInfo(type);
+        return true;
+    }
+
+    @Override
+    @NonNull
+    public synchronized MediaRoute2Info getDeviceRoute() {
+        if (mSelectedRoute != null) {
+            return mSelectedRoute;
+        }
+        return mDeviceRoute;
+    }
+
+    @Override
+    public synchronized boolean updateVolume(int volume) {
+        if (mDeviceVolume == volume) {
+            return false;
+        }
+
+        mDeviceVolume = volume;
+
+        if (mSelectedRoute != null) {
+            mSelectedRoute = new MediaRoute2Info.Builder(mSelectedRoute)
+                    .setVolume(volume)
+                    .build();
+        }
+
+        mDeviceRoute = new MediaRoute2Info.Builder(mDeviceRoute)
+                .setVolume(volume)
+                .build();
+
+        return true;
+    }
+
+    @NonNull
+    private MediaRoute2Info createRouteFromAudioInfo(@Nullable AudioRoutesInfo newRoutes) {
+        int type = TYPE_BUILTIN_SPEAKER;
+
+        if (newRoutes != null) {
+            if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADPHONES) != 0) {
+                type = TYPE_WIRED_HEADPHONES;
+            } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADSET) != 0) {
+                type = TYPE_WIRED_HEADSET;
+            } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_DOCK_SPEAKERS) != 0) {
+                type = TYPE_DOCK;
+            } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HDMI) != 0) {
+                type = TYPE_HDMI;
+            } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_USB) != 0) {
+                type = TYPE_USB_DEVICE;
+            }
+        }
+
+        return createRouteFromAudioInfo(type);
+    }
+
+    @NonNull
+    private MediaRoute2Info createRouteFromAudioInfo(@MediaRoute2Info.Type int type) {
+        int name = R.string.default_audio_route_name;
+
+        switch (type) {
+            case TYPE_WIRED_HEADPHONES:
+            case TYPE_WIRED_HEADSET:
+                name = R.string.default_audio_route_name_headphones;
+                break;
+            case TYPE_DOCK:
+                name = R.string.default_audio_route_name_dock_speakers;
+                break;
+            case TYPE_HDMI:
+                name = R.string.default_audio_route_name_external_device;
+                break;
+            case TYPE_USB_DEVICE:
+                name = R.string.default_audio_route_name_usb;
+                break;
+        }
+
+        synchronized (this) {
+            return new MediaRoute2Info.Builder(
+                    DEVICE_ROUTE_ID, mContext.getResources().getText(name).toString())
+                    .setVolumeHandling(mAudioManager.isVolumeFixed()
+                            ? MediaRoute2Info.PLAYBACK_VOLUME_FIXED
+                            : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
+                    .setVolume(mDeviceVolume)
+                    .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
+                    .setType(type)
+                    .addFeature(FEATURE_LIVE_AUDIO)
+                    .addFeature(FEATURE_LIVE_VIDEO)
+                    .addFeature(FEATURE_LOCAL_PLAYBACK)
+                    .setConnectionState(MediaRoute2Info.CONNECTION_STATE_CONNECTED)
+                    .build();
+        }
+    }
+
+    /**
+     * Checks if the given type is a device route.
+     *
+     * <p>Device route means a route which is either built-in or wired to the current device.
+     *
+     * @param type specifies the type of the device.
+     * @return {@code true} if the device is wired or built-in and {@code false} otherwise.
+     */
+    private boolean isDeviceRouteType(@MediaRoute2Info.Type int type) {
+        switch (type) {
+            case TYPE_BUILTIN_SPEAKER:
+            case TYPE_WIRED_HEADPHONES:
+            case TYPE_WIRED_HEADSET:
+            case TYPE_DOCK:
+            case TYPE_HDMI:
+            case TYPE_USB_DEVICE:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    private class AudioRoutesObserver extends IAudioRoutesObserver.Stub {
+
+        @Override
+        public void dispatchAudioRoutesChanged(AudioRoutesInfo newAudioRoutes) {
+            boolean isDeviceRouteChanged;
+            MediaRoute2Info deviceRoute = createRouteFromAudioInfo(newAudioRoutes);
+
+            synchronized (AudioPoliciesDeviceRouteController.this) {
+                mDeviceRoute = deviceRoute;
+                isDeviceRouteChanged = mSelectedRoute == null;
+            }
+
+            if (isDeviceRouteChanged) {
+                mOnDeviceRouteChangedListener.onDeviceRouteChanged(deviceRoute);
+            }
+        }
+    }
+
+}
diff --git a/services/core/java/com/android/server/media/DeviceRouteController.java b/services/core/java/com/android/server/media/DeviceRouteController.java
index 8bd6416..d7893ee 100644
--- a/services/core/java/com/android/server/media/DeviceRouteController.java
+++ b/services/core/java/com/android/server/media/DeviceRouteController.java
@@ -16,32 +16,14 @@
 
 package com.android.server.media;
 
-import static android.media.MediaRoute2Info.FEATURE_LIVE_AUDIO;
-import static android.media.MediaRoute2Info.FEATURE_LIVE_VIDEO;
-import static android.media.MediaRoute2Info.FEATURE_LOCAL_PLAYBACK;
-import static android.media.MediaRoute2Info.TYPE_BUILTIN_SPEAKER;
-import static android.media.MediaRoute2Info.TYPE_DOCK;
-import static android.media.MediaRoute2Info.TYPE_HDMI;
-import static android.media.MediaRoute2Info.TYPE_USB_DEVICE;
-import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES;
-import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
 import android.media.AudioManager;
-import android.media.AudioRoutesInfo;
 import android.media.IAudioRoutesObserver;
 import android.media.IAudioService;
 import android.media.MediaRoute2Info;
-import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.util.Slog;
-
-import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.util.Objects;
 
 /**
  * Controls device routes.
@@ -49,145 +31,65 @@
  * <p>A device route is a system wired route, for example, built-in speaker, wired
  * headsets and headphones, dock, hdmi, or usb devices.
  *
- * <p>Thread safe.
- *
  * @see SystemMediaRoute2Provider
  */
-/* package */ final class DeviceRouteController {
+/* package */ interface DeviceRouteController {
 
-    private static final String TAG = "WiredRoutesController";
-
-    private static final String DEVICE_ROUTE_ID = "DEVICE_ROUTE";
-
-    @NonNull
-    private final Context mContext;
-    @NonNull
-    private final AudioManager mAudioManager;
-    @NonNull
-    private final IAudioService mAudioService;
-
-    @NonNull
-    private final OnDeviceRouteChangedListener mOnDeviceRouteChangedListener;
-    @NonNull
-    private final AudioRoutesObserver mAudioRoutesObserver = new AudioRoutesObserver();
-
-    private int mDeviceVolume;
-    private MediaRoute2Info mDeviceRoute;
-
+    /**
+     * Returns a new instance of {@link DeviceRouteController}.
+     */
     /* package */ static DeviceRouteController createInstance(@NonNull Context context,
             @NonNull OnDeviceRouteChangedListener onDeviceRouteChangedListener) {
         AudioManager audioManager = context.getSystemService(AudioManager.class);
         IAudioService audioService = IAudioService.Stub.asInterface(
                 ServiceManager.getService(Context.AUDIO_SERVICE));
 
-        return new DeviceRouteController(context,
+        return new LegacyDeviceRouteController(context,
                 audioManager,
                 audioService,
                 onDeviceRouteChangedListener);
     }
 
-    @VisibleForTesting
-    /* package */ DeviceRouteController(@NonNull Context context,
-            @NonNull AudioManager audioManager,
-            @NonNull IAudioService audioService,
-            @NonNull OnDeviceRouteChangedListener onDeviceRouteChangedListener) {
-        Objects.requireNonNull(context);
-        Objects.requireNonNull(audioManager);
-        Objects.requireNonNull(audioService);
-        Objects.requireNonNull(onDeviceRouteChangedListener);
+    /**
+     * Select the route with the given built-in or wired {@link MediaRoute2Info.Type}.
+     *
+     * <p>If the type is {@code null} then unselects the route and falls back to the default device
+     * route observed from
+     * {@link com.android.server.audio.AudioService#startWatchingRoutes(IAudioRoutesObserver)}.
+     *
+     * @param type device type. May be {@code null} to unselect currently selected route.
+     * @return whether the selection succeeds. If the selection fails the state of the controller
+     * remains intact.
+     */
+    boolean selectRoute(@Nullable @MediaRoute2Info.Type Integer type);
 
-        mContext = context;
-        mOnDeviceRouteChangedListener = onDeviceRouteChangedListener;
-
-        mAudioManager = audioManager;
-        mAudioService = audioService;
-
-        AudioRoutesInfo newAudioRoutes = null;
-        try {
-            newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver);
-        } catch (RemoteException e) {
-            Slog.w(TAG, "Cannot connect to audio service to start listen to routes", e);
-        }
-
-        mDeviceRoute = createRouteFromAudioInfo(newAudioRoutes);
-    }
-
+    /**
+     * Returns currently selected device (built-in or wired) route.
+     *
+     * @return non-null device route.
+     */
     @NonNull
-    /* package */ synchronized MediaRoute2Info getDeviceRoute() {
-        return mDeviceRoute;
-    }
+    MediaRoute2Info getDeviceRoute();
 
-    /* package */ synchronized boolean updateVolume(int volume) {
-        if (mDeviceVolume == volume) {
-            return false;
-        }
+    /**
+     * Updates device route volume.
+     *
+     * @param volume specifies a volume for the device route or 0 for unknown.
+     * @return {@code true} if updated successfully and {@code false} otherwise.
+     */
+    boolean updateVolume(int volume);
 
-        mDeviceVolume = volume;
-        mDeviceRoute = new MediaRoute2Info.Builder(mDeviceRoute)
-                .setVolume(volume)
-                .build();
+    /**
+     * Interface for receiving events when device route has changed.
+     */
+    interface OnDeviceRouteChangedListener {
 
-        return true;
-    }
-
-    private MediaRoute2Info createRouteFromAudioInfo(@Nullable AudioRoutesInfo newRoutes) {
-        int name = R.string.default_audio_route_name;
-        int type = TYPE_BUILTIN_SPEAKER;
-
-        if (newRoutes != null) {
-            if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADPHONES) != 0) {
-                type = TYPE_WIRED_HEADPHONES;
-                name = com.android.internal.R.string.default_audio_route_name_headphones;
-            } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADSET) != 0) {
-                type = TYPE_WIRED_HEADSET;
-                name = com.android.internal.R.string.default_audio_route_name_headphones;
-            } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_DOCK_SPEAKERS) != 0) {
-                type = TYPE_DOCK;
-                name = com.android.internal.R.string.default_audio_route_name_dock_speakers;
-            } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HDMI) != 0) {
-                type = TYPE_HDMI;
-                name = com.android.internal.R.string.default_audio_route_name_external_device;
-            } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_USB) != 0) {
-                type = TYPE_USB_DEVICE;
-                name = com.android.internal.R.string.default_audio_route_name_usb;
-            }
-        }
-
-        synchronized (this) {
-            return new MediaRoute2Info.Builder(
-                    DEVICE_ROUTE_ID, mContext.getResources().getText(name).toString())
-                    .setVolumeHandling(mAudioManager.isVolumeFixed()
-                            ? MediaRoute2Info.PLAYBACK_VOLUME_FIXED
-                            : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
-                    .setVolume(mDeviceVolume)
-                    .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
-                    .setType(type)
-                    .addFeature(FEATURE_LIVE_AUDIO)
-                    .addFeature(FEATURE_LIVE_VIDEO)
-                    .addFeature(FEATURE_LOCAL_PLAYBACK)
-                    .setConnectionState(MediaRoute2Info.CONNECTION_STATE_CONNECTED)
-                    .build();
-        }
-    }
-
-    private void notifyDeviceRouteUpdate(@NonNull MediaRoute2Info deviceRoute) {
-        mOnDeviceRouteChangedListener.onDeviceRouteChanged(deviceRoute);
-    }
-
-    /* package */ interface OnDeviceRouteChangedListener {
+        /**
+         * Called when device route has changed.
+         *
+         * @param deviceRoute non-null device route.
+         */
         void onDeviceRouteChanged(@NonNull MediaRoute2Info deviceRoute);
     }
 
-    private class AudioRoutesObserver extends IAudioRoutesObserver.Stub {
-
-        @Override
-        public void dispatchAudioRoutesChanged(AudioRoutesInfo newAudioRoutes) {
-            MediaRoute2Info deviceRoute = createRouteFromAudioInfo(newAudioRoutes);
-            synchronized (DeviceRouteController.this) {
-                mDeviceRoute = deviceRoute;
-            }
-            notifyDeviceRouteUpdate(deviceRoute);
-        }
-    }
-
 }
diff --git a/services/core/java/com/android/server/media/LegacyDeviceRouteController.java b/services/core/java/com/android/server/media/LegacyDeviceRouteController.java
new file mode 100644
index 0000000..971d11f
--- /dev/null
+++ b/services/core/java/com/android/server/media/LegacyDeviceRouteController.java
@@ -0,0 +1,184 @@
+/*
+ * 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.server.media;
+
+import static android.media.MediaRoute2Info.FEATURE_LIVE_AUDIO;
+import static android.media.MediaRoute2Info.FEATURE_LIVE_VIDEO;
+import static android.media.MediaRoute2Info.FEATURE_LOCAL_PLAYBACK;
+import static android.media.MediaRoute2Info.TYPE_BUILTIN_SPEAKER;
+import static android.media.MediaRoute2Info.TYPE_DOCK;
+import static android.media.MediaRoute2Info.TYPE_HDMI;
+import static android.media.MediaRoute2Info.TYPE_USB_DEVICE;
+import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES;
+import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.media.AudioManager;
+import android.media.AudioRoutesInfo;
+import android.media.IAudioRoutesObserver;
+import android.media.IAudioService;
+import android.media.MediaRoute2Info;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Objects;
+
+/**
+ * Controls device routes.
+ *
+ * <p>A device route is a system wired route, for example, built-in speaker, wired
+ * headsets and headphones, dock, hdmi, or usb devices.
+ *
+ * <p>Thread safe.
+ *
+ * @see SystemMediaRoute2Provider
+ */
+/* package */ final class LegacyDeviceRouteController implements DeviceRouteController {
+
+    private static final String TAG = "LDeviceRouteController";
+
+    private static final String DEVICE_ROUTE_ID = "DEVICE_ROUTE";
+
+    @NonNull
+    private final Context mContext;
+    @NonNull
+    private final AudioManager mAudioManager;
+    @NonNull
+    private final IAudioService mAudioService;
+
+    @NonNull
+    private final OnDeviceRouteChangedListener mOnDeviceRouteChangedListener;
+    @NonNull
+    private final AudioRoutesObserver mAudioRoutesObserver = new AudioRoutesObserver();
+
+    private int mDeviceVolume;
+    private MediaRoute2Info mDeviceRoute;
+
+    @VisibleForTesting
+    /* package */ LegacyDeviceRouteController(@NonNull Context context,
+            @NonNull AudioManager audioManager,
+            @NonNull IAudioService audioService,
+            @NonNull OnDeviceRouteChangedListener onDeviceRouteChangedListener) {
+        Objects.requireNonNull(context);
+        Objects.requireNonNull(audioManager);
+        Objects.requireNonNull(audioService);
+        Objects.requireNonNull(onDeviceRouteChangedListener);
+
+        mContext = context;
+        mOnDeviceRouteChangedListener = onDeviceRouteChangedListener;
+
+        mAudioManager = audioManager;
+        mAudioService = audioService;
+
+        AudioRoutesInfo newAudioRoutes = null;
+        try {
+            newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Cannot connect to audio service to start listen to routes", e);
+        }
+
+        mDeviceRoute = createRouteFromAudioInfo(newAudioRoutes);
+    }
+
+    @Override
+    public boolean selectRoute(@Nullable Integer type) {
+        // No-op as the controller does not support selection from the outside of the class.
+        return false;
+    }
+
+    @Override
+    @NonNull
+    public synchronized MediaRoute2Info getDeviceRoute() {
+        return mDeviceRoute;
+    }
+
+    @Override
+    public synchronized boolean updateVolume(int volume) {
+        if (mDeviceVolume == volume) {
+            return false;
+        }
+
+        mDeviceVolume = volume;
+        mDeviceRoute = new MediaRoute2Info.Builder(mDeviceRoute)
+                .setVolume(volume)
+                .build();
+
+        return true;
+    }
+
+    private MediaRoute2Info createRouteFromAudioInfo(@Nullable AudioRoutesInfo newRoutes) {
+        int name = R.string.default_audio_route_name;
+        int type = TYPE_BUILTIN_SPEAKER;
+
+        if (newRoutes != null) {
+            if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADPHONES) != 0) {
+                type = TYPE_WIRED_HEADPHONES;
+                name = com.android.internal.R.string.default_audio_route_name_headphones;
+            } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADSET) != 0) {
+                type = TYPE_WIRED_HEADSET;
+                name = com.android.internal.R.string.default_audio_route_name_headphones;
+            } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_DOCK_SPEAKERS) != 0) {
+                type = TYPE_DOCK;
+                name = com.android.internal.R.string.default_audio_route_name_dock_speakers;
+            } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HDMI) != 0) {
+                type = TYPE_HDMI;
+                name = com.android.internal.R.string.default_audio_route_name_external_device;
+            } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_USB) != 0) {
+                type = TYPE_USB_DEVICE;
+                name = com.android.internal.R.string.default_audio_route_name_usb;
+            }
+        }
+
+        synchronized (this) {
+            return new MediaRoute2Info.Builder(
+                    DEVICE_ROUTE_ID, mContext.getResources().getText(name).toString())
+                    .setVolumeHandling(mAudioManager.isVolumeFixed()
+                            ? MediaRoute2Info.PLAYBACK_VOLUME_FIXED
+                            : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
+                    .setVolume(mDeviceVolume)
+                    .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
+                    .setType(type)
+                    .addFeature(FEATURE_LIVE_AUDIO)
+                    .addFeature(FEATURE_LIVE_VIDEO)
+                    .addFeature(FEATURE_LOCAL_PLAYBACK)
+                    .setConnectionState(MediaRoute2Info.CONNECTION_STATE_CONNECTED)
+                    .build();
+        }
+    }
+
+    private void notifyDeviceRouteUpdate(@NonNull MediaRoute2Info deviceRoute) {
+        mOnDeviceRouteChangedListener.onDeviceRouteChanged(deviceRoute);
+    }
+
+    private class AudioRoutesObserver extends IAudioRoutesObserver.Stub {
+
+        @Override
+        public void dispatchAudioRoutesChanged(AudioRoutesInfo newAudioRoutes) {
+            MediaRoute2Info deviceRoute = createRouteFromAudioInfo(newAudioRoutes);
+            synchronized (LegacyDeviceRouteController.this) {
+                mDeviceRoute = deviceRoute;
+            }
+            notifyDeviceRouteUpdate(deviceRoute);
+        }
+    }
+
+}
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 53e841d..73440b7 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -892,6 +892,7 @@
         return allowedComponents;
     }
 
+    @NonNull
     protected List<String> getAllowedPackages(int userId) {
         final List<String> allowedPackages = new ArrayList<>();
         synchronized (mApproved) {
@@ -1181,25 +1182,6 @@
         return installed;
     }
 
-    protected Set<String> getAllowedPackages() {
-        final Set<String> allowedPackages = new ArraySet<>();
-        synchronized (mApproved) {
-            for (int k = 0; k < mApproved.size(); k++) {
-                ArrayMap<Boolean, ArraySet<String>> allowedByType = mApproved.valueAt(k);
-                for (int i = 0; i < allowedByType.size(); i++) {
-                    final ArraySet<String> allowed = allowedByType.valueAt(i);
-                    for (int j = 0; j < allowed.size(); j++) {
-                        String pkgName = getPackageName(allowed.valueAt(j));
-                        if (!TextUtils.isEmpty(pkgName)) {
-                            allowedPackages.add(pkgName);
-                        }
-                    }
-                }
-            }
-        }
-        return allowedPackages;
-    }
-
     private void trimApprovedListsAccordingToInstalledServices(int userId) {
         synchronized (mApproved) {
             final ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.get(userId);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 0d39457..53b03d5 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2709,16 +2709,18 @@
     }
 
     private void sendRegisteredOnlyBroadcast(String action) {
-        Intent intent = new Intent(action);
-        getContext().sendBroadcastAsUser(intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
-                UserHandle.ALL, null);
+        int[] userIds = mUmInternal.getProfileIds(mAmi.getCurrentUserId(), true);
+        Intent intent = new Intent(action).addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+        for (int userId : userIds) {
+            getContext().sendBroadcastAsUser(intent, UserHandle.of(userId), null);
+        }
         // explicitly send the broadcast to all DND packages, even if they aren't currently running
-        intent.setFlags(0);
-        final Set<String> dndApprovedPackages = mConditionProviders.getAllowedPackages();
-        for (String pkg : dndApprovedPackages) {
-            intent.setPackage(pkg);
-            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-            getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
+        for (int userId : userIds) {
+            for (String pkg : mConditionProviders.getAllowedPackages(userId)) {
+                Intent pkgIntent = new Intent(action).setPackage(pkg).setFlags(
+                        Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+                getContext().sendBroadcastAsUser(pkgIntent, UserHandle.of(userId));
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 45dc49d..37a59da 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2977,7 +2977,7 @@
                 }
                 break;
             case KeyEvent.KEYCODE_H:
-                if (down && event.isMetaPressed()) {
+                if (event.isMetaPressed()) {
                     return handleHomeShortcuts(displayId, focusedToken, event);
                 }
                 break;
@@ -3018,11 +3018,6 @@
                 if (down && event.isMetaPressed() && event.isCtrlPressed() && repeatCount == 0) {
                     enterStageSplitFromRunningApp(true /* leftOrTop */);
                     return key_consumed;
-                } else if (!down && event.isMetaPressed()) {
-                    boolean backKeyHandled = backKeyPress();
-                    if (backKeyHandled) {
-                        return key_consumed;
-                    }
                 }
                 break;
             case KeyEvent.KEYCODE_DPAD_RIGHT:
@@ -3031,14 +3026,6 @@
                     return key_consumed;
                 }
                 break;
-            case KeyEvent.KEYCODE_GRAVE:
-                if (!down && event.isMetaPressed()) {
-                    boolean backKeyHandled = backKeyPress();
-                    if (backKeyHandled) {
-                        return key_consumed;
-                    }
-                }
-                break;
             case KeyEvent.KEYCODE_SLASH:
                 if (down && repeatCount == 0 && event.isMetaPressed() && !keyguardOn) {
                     toggleKeyboardShortcutsMenu(event.getDeviceId());
diff --git a/services/core/java/com/android/server/power/stats/CpuWakeupStats.java b/services/core/java/com/android/server/power/stats/CpuWakeupStats.java
index e8c0e59..706aedc 100644
--- a/services/core/java/com/android/server/power/stats/CpuWakeupStats.java
+++ b/services/core/java/com/android/server/power/stats/CpuWakeupStats.java
@@ -126,7 +126,10 @@
     /** Notes a wakeup reason as reported by SuspendControlService to battery stats. */
     public synchronized void noteWakeupTimeAndReason(long elapsedRealtime, long uptime,
             String rawReason) {
-        final Wakeup parsedWakeup = new Wakeup(rawReason, elapsedRealtime, uptime);
+        final Wakeup parsedWakeup = Wakeup.parseWakeup(rawReason, elapsedRealtime, uptime);
+        if (parsedWakeup == null) {
+            return;
+        }
         mWakeupEvents.put(elapsedRealtime, parsedWakeup);
         attemptAttributionFor(parsedWakeup);
         // Assuming that wakeups always arrive in monotonically increasing elapsedRealtime order,
@@ -451,28 +454,25 @@
         private static final String PARSER_TAG = "CpuWakeupStats.Wakeup";
         private static final String ABORT_REASON_PREFIX = "Abort";
         private static final Pattern sIrqPattern = Pattern.compile("^(\\d+)\\s+(\\S+)");
-
-        String mRawReason;
         long mElapsedMillis;
         long mUptimeMillis;
         IrqDevice[] mDevices;
 
-        Wakeup(String rawReason, long elapsedMillis, long uptimeMillis) {
-            mRawReason = rawReason;
+        private Wakeup(IrqDevice[] devices, long elapsedMillis, long uptimeMillis) {
             mElapsedMillis = elapsedMillis;
             mUptimeMillis = uptimeMillis;
-            mDevices = parseIrqDevices(rawReason);
+            mDevices = devices;
         }
 
-        private static IrqDevice[] parseIrqDevices(String rawReason) {
+        static Wakeup parseWakeup(String rawReason, long elapsedMillis, long uptimeMillis) {
             final String[] components = rawReason.split(":");
             if (ArrayUtils.isEmpty(components) || components[0].startsWith(ABORT_REASON_PREFIX)) {
-                // We don't support parsing aborts yet.
+                // Accounting of aborts is not supported yet.
                 return null;
             }
 
             int parsedDeviceCount = 0;
-            IrqDevice[] parsedDevices = new IrqDevice[components.length];
+            final IrqDevice[] parsedDevices = new IrqDevice[components.length];
 
             for (String component : components) {
                 final Matcher matcher = sIrqPattern.matcher(component.trim());
@@ -490,14 +490,17 @@
                     parsedDevices[parsedDeviceCount++] = new IrqDevice(line, device);
                 }
             }
-            return (parsedDeviceCount > 0) ? Arrays.copyOf(parsedDevices, parsedDeviceCount) : null;
+            if (parsedDeviceCount == 0) {
+                return null;
+            }
+            return new Wakeup(Arrays.copyOf(parsedDevices, parsedDeviceCount), elapsedMillis,
+                    uptimeMillis);
         }
 
         @Override
         public String toString() {
             return "Wakeup{"
-                    + "mRawReason='" + mRawReason + '\''
-                    + ", mElapsedMillis=" + mElapsedMillis
+                    + "mElapsedMillis=" + mElapsedMillis
                     + ", mUptimeMillis=" + TimeUtils.formatDuration(mUptimeMillis)
                     + ", mDevices=" + Arrays.toString(mDevices)
                     + '}';
@@ -514,7 +517,7 @@
 
             @Override
             public String toString() {
-                return "IrqDevice{" + "mLine=" + mLine + ", mDevice='" + mDevice + '\'' + '}';
+                return "IrqDevice{" + "mLine=" + mLine + ", mDevice=\'" + mDevice + '\'' + '}';
             }
         }
     }
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 7a73359..c9eef38 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -923,6 +923,8 @@
             if (DEBUG) Slog.v(TAG, "Adding window token: " + mToken);
             mWindowManagerInternal.addWindowToken(mToken, TYPE_WALLPAPER, mDisplayId,
                     null /* options */);
+            mWindowManagerInternal.setWallpaperShowWhenLocked(
+                    mToken, (wallpaper.mWhich & FLAG_LOCK) != 0);
             final DisplayData wpdData =
                     mWallpaperDisplayHelper.getDisplayDataOrCreate(mDisplayId);
             try {
@@ -1415,12 +1417,13 @@
                         try {
                             if (connector.mEngine != null) {
                                 connector.mEngine.setWallpaperFlags(which);
+                                mWindowManagerInternal.setWallpaperShowWhenLocked(
+                                        connector.mToken, (which & FLAG_LOCK) != 0);
                             }
                         } catch (RemoteException e) {
                             Slog.e(TAG, "Failed to update wallpaper engine flags", e);
                         }
-                    }
-            );
+                    });
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 9def87c..37e4890 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -5228,8 +5228,7 @@
         mTaskSupervisor.mAppVisibilitiesChangedSinceLastPause = true;
     }
 
-    @VisibleForTesting
-    void setVisibility(boolean visible, boolean deferHidingClient) {
+    private void setVisibility(boolean visible, boolean deferHidingClient) {
         final AppTransition appTransition = getDisplayContent().mAppTransition;
 
         // Don't set visibility to false if we were already not visible. This prevents WM from
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index f73c68a..939cf1a 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -888,8 +888,11 @@
         } else {
             int animAttr = mapOpenCloseTransitTypes(transit, enter);
             if (animAttr != 0) {
-                a = loadCustomActivityAnimation(animAttr, enter, container);
-                if (a == null) {
+                final CustomAppTransition customAppTransition =
+                        getCustomAppTransition(animAttr, container);
+                if (customAppTransition != null) {
+                    a = loadCustomActivityAnimation(customAppTransition, enter, container);
+                } else {
                     if (canCustomizeAppTransition) {
                         a = loadAnimationAttr(lp, animAttr, transit);
                     } else {
@@ -911,7 +914,7 @@
         return a;
     }
 
-    Animation loadCustomActivityAnimation(int animAttr, boolean enter, WindowContainer container) {
+    CustomAppTransition getCustomAppTransition(int animAttr, WindowContainer container) {
         ActivityRecord customAnimationSource = container.asActivityRecord();
         if (customAnimationSource == null) {
             return null;
@@ -927,31 +930,28 @@
                 return null;
             }
         }
-        final CustomAppTransition custom;
         switch (animAttr) {
             case WindowAnimation_activityOpenEnterAnimation:
             case WindowAnimation_activityOpenExitAnimation:
-                custom = customAnimationSource.getCustomAnimation(true /* open */);
-                break;
+                return customAnimationSource.getCustomAnimation(true /* open */);
             case WindowAnimation_activityCloseEnterAnimation:
             case WindowAnimation_activityCloseExitAnimation:
-                custom = customAnimationSource.getCustomAnimation(false /* open */);
-                break;
-            default:
-                return null;
-        }
-        if (custom != null) {
-            final Animation a = mTransitionAnimation.loadAppTransitionAnimation(
-                    customAnimationSource.packageName, enter
-                            ? custom.mEnterAnim : custom.mExitAnim);
-            if (a != null && custom.mBackgroundColor != 0) {
-                a.setBackdropColor(custom.mBackgroundColor);
-                a.setShowBackdrop(true);
-            }
-            return a;
+                return customAnimationSource.getCustomAnimation(false /* open */);
         }
         return null;
     }
+    private Animation loadCustomActivityAnimation(@NonNull CustomAppTransition custom,
+            boolean enter, WindowContainer container) {
+        final ActivityRecord customAnimationSource = container.asActivityRecord();
+        final Animation a = mTransitionAnimation.loadAppTransitionAnimation(
+                customAnimationSource.packageName, enter
+                        ? custom.mEnterAnim : custom.mExitAnim);
+        if (a != null && custom.mBackgroundColor != 0) {
+            a.setBackdropColor(custom.mBackgroundColor);
+            a.setShowBackdrop(true);
+        }
+        return a;
+    }
 
     int getAppRootTaskClipMode() {
         return mNextAppTransitionRequests.contains(TRANSIT_RELAUNCH)
diff --git a/services/core/java/com/android/server/wm/AsyncRotationController.java b/services/core/java/com/android/server/wm/AsyncRotationController.java
index 0dc6e0f..5c9c813 100644
--- a/services/core/java/com/android/server/wm/AsyncRotationController.java
+++ b/services/core/java/com/android/server/wm/AsyncRotationController.java
@@ -364,6 +364,7 @@
 
     /** Hides the window immediately until it is drawn in new rotation. */
     void hideImmediately(WindowToken windowToken) {
+        if (isTargetToken(windowToken)) return;
         final boolean original = mHideImmediately;
         mHideImmediately = true;
         final Operation op = new Operation(Operation.ACTION_FADE);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 947edde..87f5703b 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -4158,13 +4158,13 @@
 
     /** @see WindowManagerInternal#onToggleImeRequested */
     void onShowImeRequested() {
-        if (mImeLayeringTarget == null || mInputMethodWindow == null) {
+        if (mInputMethodWindow == null) {
             return;
         }
         // If IME window will be shown on the rotated activity, share the transformed state to
         // IME window so it can compute rotated frame with rotated configuration.
-        if (mImeLayeringTarget.mToken.isFixedRotationTransforming()) {
-            mInputMethodWindow.mToken.linkFixedRotationTransform(mImeLayeringTarget.mToken);
+        if (mFixedRotationLaunchingApp != null) {
+            mInputMethodWindow.mToken.linkFixedRotationTransform(mFixedRotationLaunchingApp);
             // Hide the window until the rotation is done to avoid intermediate artifacts if the
             // parent surface of IME container is changed.
             if (mAsyncRotationController != null) {
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 210a7d9..c1f2b2b 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -580,6 +580,13 @@
             // Notification shade has control anyways, no reason to force anything.
             return focusedWin;
         }
+        if (focusedWin != null) {
+            final InsetsSourceProvider provider = focusedWin.getControllableInsetProvider();
+            if (provider != null && provider.getSource().getType() == Type.navigationBars()) {
+                // Navigation bar has control if it is focused.
+                return focusedWin;
+            }
+        }
         if (mPolicy.isForceShowNavigationBarEnabled() && focusedWin != null
                 && focusedWin.getActivityType() == ACTIVITY_TYPE_STANDARD) {
             // When "force show navigation bar" is enabled, it means both force visible is true, and
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 16541c1..2b848d5 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -37,6 +37,7 @@
 import static com.android.server.wm.WindowManagerService.H.WALLPAPER_DRAW_PENDING_TIMEOUT;
 
 import android.annotation.Nullable;
+import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -55,6 +56,7 @@
 import android.view.animation.Animation;
 import android.window.ScreenCapture;
 
+import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.ProtoLogImpl;
 import com.android.internal.protolog.common.ProtoLog;
@@ -72,7 +74,7 @@
 class WallpaperController {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "WallpaperController" : TAG_WM;
     private WindowManagerService mService;
-    private final DisplayContent mDisplayContent;
+    private DisplayContent mDisplayContent;
 
     private final ArrayList<WallpaperWindowToken> mWallpaperTokens = new ArrayList<>();
 
@@ -120,9 +122,19 @@
 
     private boolean mShouldOffsetWallpaperCenter;
 
+    final boolean mEnableSeparateLockScreenEngine;
+
     private final ToBooleanFunction<WindowState> mFindWallpaperTargetFunction = w -> {
         if ((w.mAttrs.type == TYPE_WALLPAPER)) {
             if (mFindResults.topWallpaper == null || mFindResults.resetTopWallpaper) {
+                WallpaperWindowToken token = w.mToken.asWallpaperToken();
+                if (token == null) {
+                    Slog.w(TAG, "Window " + w + " has wallpaper type but not wallpaper token");
+                    return false;
+                }
+                if (!token.canShowWhenLocked() && mDisplayContent.isKeyguardLocked()) {
+                    return false;
+                }
                 mFindResults.setTopWallpaper(w);
                 mFindResults.resetTopWallpaper = false;
             }
@@ -249,11 +261,14 @@
     WallpaperController(WindowManagerService service, DisplayContent displayContent) {
         mService = service;
         mDisplayContent = displayContent;
-        mMaxWallpaperScale = service.mContext.getResources()
-                .getFloat(com.android.internal.R.dimen.config_wallpaperMaxScale);
-        mShouldOffsetWallpaperCenter = service.mContext.getResources()
-                .getBoolean(
+        Resources resources = service.mContext.getResources();
+        mMaxWallpaperScale =
+                resources.getFloat(com.android.internal.R.dimen.config_wallpaperMaxScale);
+        mShouldOffsetWallpaperCenter =
+                resources.getBoolean(
                         com.android.internal.R.bool.config_offsetWallpaperToCenterOfLargestDisplay);
+        mEnableSeparateLockScreenEngine =
+                resources.getBoolean(R.bool.config_independentLockscreenLiveWallpaper);
     }
 
     void resetLargestDisplay(Display display) {
@@ -753,10 +768,10 @@
         result.setWallpaperTarget(wallpaperTarget);
     }
 
-    private void updateWallpaperTokens(boolean visible) {
+    private void updateWallpaperTokens(boolean visibility, boolean locked) {
         for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
             final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx);
-            token.updateWallpaperWindows(visible);
+            token.updateWallpaperWindows(visibility && (!locked || token.canShowWhenLocked()));
         }
     }
 
@@ -794,7 +809,13 @@
             }
         }
 
-        updateWallpaperTokens(visible);
+        // Keep both wallpapers visible unless the keyguard is locked (then hide private wp)
+        updateWallpaperTokens(visible, mDisplayContent.isKeyguardLocked());
+
+        if (DEBUG_WALLPAPER) {
+            Slog.v(TAG, "adjustWallpaperWindows: wallpaper visibility " + visible
+                    + ", lock visibility " + mDisplayContent.isKeyguardLocked());
+        }
 
         if (visible && mLastFrozen != mFindResults.isWallpaperTargetForLetterbox) {
             mLastFrozen = mFindResults.isWallpaperTargetForLetterbox;
@@ -896,7 +917,6 @@
         mWallpaperTokens.remove(token);
     }
 
-
     @VisibleForTesting
     boolean canScreenshotWallpaper() {
         return canScreenshotWallpaper(getTopVisibleWallpaper());
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index 8708f73..17ab551 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -76,14 +76,18 @@
             return;
         }
         mShowWhenLocked = showWhenLocked;
+        if (mDisplayContent.mWallpaperController.mEnableSeparateLockScreenEngine) {
+            // Move the window token to the front (private) or back (showWhenLocked). This is
+            // possible
+            // because the DisplayArea underneath TaskDisplayArea only contains TYPE_WALLPAPER
+            // windows.
+            final int position = showWhenLocked ? POSITION_BOTTOM : POSITION_TOP;
 
-        // Move the window token to the front (private) or back (showWhenLocked). This is possible
-        // because the DisplayArea underneath TaskDisplayArea only contains TYPE_WALLPAPER windows.
-        final int position = showWhenLocked ? POSITION_BOTTOM : POSITION_TOP;
-
-        // Note: Moving all the way to the front or back breaks ordering based on addition times.
-        // We should never have more than one non-animating token of each type.
-        getParent().positionChildAt(position, this /* child */, false  /*includingParents */);
+            // Note: Moving all the way to the front or back breaks ordering based on addition
+            // times.
+            // We should never have more than one non-animating token of each type.
+            getParent().positionChildAt(position, this /* child */, false /*includingParents */);
+        }
     }
 
     boolean canShowWhenLocked() {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 87e87b9..09f7fb6 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2342,9 +2342,11 @@
 
     @Override
     public void onConfigurationChanged(Configuration newParentConfig) {
-        mTempConfiguration.setTo(getConfiguration());
+        // Get from super to avoid using the updated global config from the override method.
+        final Configuration selfConfiguration = super.getConfiguration();
+        mTempConfiguration.setTo(selfConfiguration);
         super.onConfigurationChanged(newParentConfig);
-        final int diff = getConfiguration().diff(mTempConfiguration);
+        final int diff = selfConfiguration.diff(mTempConfiguration);
         if (diff != 0) {
             mLastConfigReportedToClient = false;
         }
diff --git a/services/credentials/java/com/android/server/credentials/ClearRequestSession.java b/services/credentials/java/com/android/server/credentials/ClearRequestSession.java
index 6bf18c2..c8ec7c2 100644
--- a/services/credentials/java/com/android/server/credentials/ClearRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/ClearRequestSession.java
@@ -119,7 +119,6 @@
     private void respondToClientWithResponseAndFinish() {
         Log.i(TAG, "respondToClientWithResponseAndFinish");
         if (isSessionCancelled()) {
-            // TODO: Differentiate btw cancelled and false
             mChosenProviderMetric.setChosenProviderStatus(
                     MetricUtilities.METRICS_PROVIDER_STATUS_FINAL_SUCCESS);
             logApiCall(ApiName.CLEAR_CREDENTIAL, /* apiStatus */
@@ -144,7 +143,6 @@
     private void respondToClientWithErrorAndFinish(String errorType, String errorMsg) {
         Log.i(TAG, "respondToClientWithErrorAndFinish");
         if (isSessionCancelled()) {
-            // TODO: Differentiate btw cancelled and false
             logApiCall(ApiName.CLEAR_CREDENTIAL, /* apiStatus */
                     ApiStatus.METRICS_API_STATUS_CLIENT_CANCELED);
             finishSession(/*propagateCancellation=*/true);
diff --git a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
index 656e44c..0c1133c 100644
--- a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
@@ -143,7 +143,6 @@
             return;
         }
         if (isSessionCancelled()) {
-            // TODO: Differentiate btw cancelled and false
             logApiCall(ApiName.CREATE_CREDENTIAL, /* apiStatus */
                     ApiStatus.METRICS_API_STATUS_CLIENT_CANCELED);
             finishSession(/*propagateCancellation=*/true);
@@ -168,7 +167,6 @@
             return;
         }
         if (isSessionCancelled()) {
-            // TODO: Differentiate btw cancelled and false
             logApiCall(ApiName.CREATE_CREDENTIAL, /* apiStatus */
                     ApiStatus.METRICS_API_STATUS_CLIENT_CANCELED);
             finishSession(/*propagateCancellation=*/true);
diff --git a/services/credentials/java/com/android/server/credentials/GetRequestSession.java b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
index ce26c88..13f4b54 100644
--- a/services/credentials/java/com/android/server/credentials/GetRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
@@ -119,7 +119,6 @@
             return;
         }
         if (isSessionCancelled()) {
-            // TODO: Differentiate btw cancelled and false
             logApiCall(ApiName.GET_CREDENTIAL, /* apiStatus */
                     ApiStatus.METRICS_API_STATUS_CLIENT_CANCELED);
             finishSession(/*propagateCancellation=*/true);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index e9c23a0..400ee1d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3212,8 +3212,12 @@
     private void sendChangedNotification(int userHandle) {
         Intent intent = new Intent(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
         intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+        Bundle options = new BroadcastOptions()
+                .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT)
+                .setDeferUntilActive(true)
+                .toBundle();
         mInjector.binderWithCleanCallingIdentity(() ->
-                mContext.sendBroadcastAsUser(intent, new UserHandle(userHandle)));
+                mContext.sendBroadcastAsUser(intent, new UserHandle(userHandle), null, options));
     }
 
     private void loadSettingsLocked(DevicePolicyData policy, int userHandle) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 850b5b6..edfe95e 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -2739,6 +2739,14 @@
         }
         t.traceEnd();
 
+        t.traceBegin("RegisterLogMteState");
+        try {
+            LogMteState.register(context);
+        } catch (Throwable e) {
+            reportWtf("RegisterLogMteState", e);
+        }
+        t.traceEnd();
+
         // Emit any pending system_server WTFs
         synchronized (SystemService.class) {
             if (sPendingWtfs != null) {
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 99da415..8a5d3a6 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
@@ -558,7 +558,7 @@
 
         // To maximize test coverage, dump current state; we're not worried
         // about the actual output, just that we don't crash
-        queue.getActive().setDeliveryState(0, BroadcastRecord.DELIVERY_SCHEDULED);
+        queue.getActive().setDeliveryState(0, BroadcastRecord.DELIVERY_SCHEDULED, "Test-driven");
         queue.dumpLocked(SystemClock.uptimeMillis(),
                 new IndentingPrintWriter(new PrintWriter(new ByteArrayOutputStream())));
 
diff --git a/services/tests/servicestests/res/xml/irq_device_map_3.xml b/services/tests/servicestests/res/xml/irq_device_map_3.xml
index 498b676..1d2a7d3 100644
--- a/services/tests/servicestests/res/xml/irq_device_map_3.xml
+++ b/services/tests/servicestests/res/xml/irq_device_map_3.xml
@@ -21,6 +21,6 @@
         <subsystem>Alarm</subsystem>
     </device>
     <device name="test.wifi.device">
-        <subsystem>undefined</subsystem>
+        <subsystem>Wifi</subsystem>
     </device>
 </irq-device-map>
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index 2967c5c..339ccd8 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -130,7 +130,6 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.List;
 import java.util.Set;
 import java.util.function.Consumer;
 
@@ -283,7 +282,7 @@
         return blockedActivities;
     }
 
-    private Intent createRestrictedActivityBlockedIntent(List displayCategories,
+    private Intent createRestrictedActivityBlockedIntent(Set<String> displayCategories,
             String targetDisplayCategory) {
         when(mDisplayManagerInternalMock.createVirtualDisplay(any(), any(), any(), any(),
                 eq(NONBLOCKED_APP_PACKAGE_NAME))).thenReturn(DISPLAY_ID_1);
@@ -1634,7 +1633,7 @@
 
     @Test
     public void nonRestrictedActivityOnRestrictedVirtualDisplay_startBlockedAlertActivity() {
-        Intent blockedAppIntent = createRestrictedActivityBlockedIntent(List.of("abc"),
+        Intent blockedAppIntent = createRestrictedActivityBlockedIntent(Set.of("abc"),
                 /* targetDisplayCategory= */ null);
         verify(mContext).startActivityAsUser(argThat(intent ->
                 intent.filterEquals(blockedAppIntent)), any(), any());
@@ -1642,7 +1641,7 @@
 
     @Test
     public void restrictedActivityOnRestrictedVirtualDisplay_doesNotStartBlockedAlertActivity() {
-        Intent blockedAppIntent = createRestrictedActivityBlockedIntent(List.of("abc"), "abc");
+        Intent blockedAppIntent = createRestrictedActivityBlockedIntent(Set.of("abc"), "abc");
         verify(mContext, never()).startActivityAsUser(argThat(intent ->
                 intent.filterEquals(blockedAppIntent)), any(), any());
     }
@@ -1650,14 +1649,14 @@
     @Test
     public void restrictedActivityOnNonRestrictedVirtualDisplay_startBlockedAlertActivity() {
         Intent blockedAppIntent = createRestrictedActivityBlockedIntent(
-                /* displayCategories= */ List.of(), "abc");
+                /* displayCategories= */ Set.of(), "abc");
         verify(mContext).startActivityAsUser(argThat(intent ->
                 intent.filterEquals(blockedAppIntent)), any(), any());
     }
 
     @Test
     public void restrictedActivityNonMatchingRestrictedVirtualDisplay_startBlockedAlertActivity() {
-        Intent blockedAppIntent = createRestrictedActivityBlockedIntent(List.of("abc"), "def");
+        Intent blockedAppIntent = createRestrictedActivityBlockedIntent(Set.of("abc"), "def");
         verify(mContext).startActivityAsUser(argThat(intent ->
                 intent.filterEquals(blockedAppIntent)), any(), any());
     }
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
index d9e4da7..2bfa44e 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
@@ -89,7 +89,7 @@
                         /* activityBlockedCallback= */ null,
                         /* secureWindowCallback= */ null,
                         /* intentListenerCallback= */ null,
-                        /* displayCategories= */ new ArrayList<>(),
+                        /* displayCategories= */ new ArraySet<>(),
                         /* showTasksInHostDeviceRecents= */ true);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 1f25da7..aaabb28 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -162,9 +162,9 @@
 import org.hamcrest.BaseMatcher;
 import org.hamcrest.Description;
 import org.hamcrest.Matcher;
-import org.junit.Ignore;
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.mockito.Mockito;
 import org.mockito.internal.util.collections.Sets;
@@ -514,7 +514,9 @@
         verify(mContext.spiedContext).sendBroadcastAsUser(
                 MockUtils.checkIntentAction(
                         DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
-                MockUtils.checkUserHandle(CALLER_USER_HANDLE));
+                MockUtils.checkUserHandle(CALLER_USER_HANDLE),
+                eq(null),
+                any(Bundle.class));
         verify(mContext.spiedContext).sendBroadcastAsUser(
                 MockUtils.checkIntentAction(
                         DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED),
@@ -793,7 +795,9 @@
         verify(mContext.spiedContext, times(1)).sendBroadcastAsUser(
                 MockUtils.checkIntentAction(
                         DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
-                MockUtils.checkUserHandle(CALLER_USER_HANDLE));
+                MockUtils.checkUserHandle(CALLER_USER_HANDLE),
+                eq(null),
+                any(Bundle.class));
 
         // Remove.  No permissions, but same user, so it'll work.
         mContext.callerPermissions.clear();
@@ -820,7 +824,9 @@
         verify(mContext.spiedContext, times(2)).sendBroadcastAsUser(
                 MockUtils.checkIntentAction(
                         DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
-                MockUtils.checkUserHandle(CALLER_USER_HANDLE));
+                MockUtils.checkUserHandle(CALLER_USER_HANDLE),
+                eq(null),
+                any(Bundle.class));
 
         // TODO Check other internal calls.
     }
@@ -846,7 +852,9 @@
         verify(mContext.spiedContext, times(2)).sendBroadcastAsUser(
                 MockUtils.checkIntentAction(
                         DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
-                MockUtils.checkUserHandle(CALLER_USER_HANDLE));
+                MockUtils.checkUserHandle(CALLER_USER_HANDLE),
+                eq(null),
+                any(Bundle.class));
 
         // Remove.  No permissions, but same user, so it'll work.
         mContext.callerPermissions.clear();
@@ -874,7 +882,9 @@
         verify(mContext.spiedContext, times(3)).sendBroadcastAsUser(
                 MockUtils.checkIntentAction(
                         DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
-                MockUtils.checkUserHandle(CALLER_USER_HANDLE));
+                MockUtils.checkUserHandle(CALLER_USER_HANDLE),
+                eq(null),
+                any(Bundle.class));
     }
 
     /**
@@ -2425,7 +2435,9 @@
         verify(mContext.spiedContext, times(2)).sendBroadcastAsUser(
                 MockUtils.checkIntentAction(
                         DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
-                MockUtils.checkUserHandle(CALLER_USER_HANDLE));
+                MockUtils.checkUserHandle(CALLER_USER_HANDLE),
+                eq(null),
+                any(Bundle.class));
         verify(mContext.spiedContext).sendBroadcastAsUser(
                 MockUtils.checkIntentAction(
                         DevicePolicyManager.ACTION_PROFILE_OWNER_CHANGED),
@@ -5886,7 +5898,9 @@
         verify(mContext.spiedContext, times(1)).sendBroadcastAsUser(
                 MockUtils.checkIntentAction(
                         DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
-                MockUtils.checkUserHandle(userHandle));
+                MockUtils.checkUserHandle(userHandle),
+                eq(null),
+                any(Bundle.class));
 
         final Intent intent = new Intent(DeviceAdminReceiver.ACTION_PASSWORD_CHANGED);
         intent.setComponent(admin1);
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index 7971fd7..de1c219 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -967,7 +967,7 @@
         final VirtualDisplayConfig.Builder builder2 = new VirtualDisplayConfig.Builder(
                 VIRTUAL_DISPLAY_NAME, width, height, dpi)
                 .setUniqueId(uniqueId2)
-                .setWindowManagerMirroring(true);
+                .setWindowManagerMirroringEnabled(true);
         final int secondDisplayId = binderService.createVirtualDisplay(builder2.build(),
                 mMockAppToken2 /* callback */, null /* projection */,
                 PACKAGE_NAME);
diff --git a/services/tests/servicestests/src/com/android/server/input/KeyboardLayoutManagerTests.kt b/services/tests/servicestests/src/com/android/server/input/KeyboardLayoutManagerTests.kt
index b2bfd2b..b660926 100644
--- a/services/tests/servicestests/src/com/android/server/input/KeyboardLayoutManagerTests.kt
+++ b/services/tests/servicestests/src/com/android/server/input/KeyboardLayoutManagerTests.kt
@@ -256,12 +256,16 @@
     @Test
     fun testNewUi_getKeyboardLayoutsForInputDevice() {
         NewSettingsApiFlag(true).use {
-            val keyboardLayouts =
-                keyboardLayoutManager.getKeyboardLayoutsForInputDevice(keyboardDevice.identifier)
-            assertEquals(
-                "New UI: getKeyboardLayoutsForInputDevice API should always return empty array",
-                0,
-                keyboardLayouts.size
+            val keyboardLayouts = keyboardLayoutManager.keyboardLayouts
+            assertNotEquals(
+                    "New UI: getKeyboardLayoutsForInputDevice API should not return empty array",
+                    0,
+                    keyboardLayouts.size
+            )
+            assertTrue(
+                    "New UI: getKeyboardLayoutsForInputDevice API should provide English(US) " +
+                            "layout",
+                    hasLayout(keyboardLayouts, ENGLISH_US_LAYOUT_DESCRIPTOR)
             )
         }
     }
diff --git a/services/tests/servicestests/src/com/android/server/media/AudioPoliciesDeviceRouteControllerTest.java b/services/tests/servicestests/src/com/android/server/media/AudioPoliciesDeviceRouteControllerTest.java
new file mode 100644
index 0000000..1e73a45
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/media/AudioPoliciesDeviceRouteControllerTest.java
@@ -0,0 +1,247 @@
+/*
+ * 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.server.media;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.media.AudioManager;
+import android.media.AudioRoutesInfo;
+import android.media.IAudioRoutesObserver;
+import android.media.MediaRoute2Info;
+import android.os.RemoteException;
+
+import com.android.internal.R;
+import com.android.server.audio.AudioService;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(JUnit4.class)
+public class AudioPoliciesDeviceRouteControllerTest {
+
+    private static final String ROUTE_NAME_DEFAULT = "default";
+    private static final String ROUTE_NAME_DOCK = "dock";
+    private static final String ROUTE_NAME_HEADPHONES = "headphones";
+
+    private static final int VOLUME_SAMPLE_1 = 25;
+
+    @Mock
+    private Context mContext;
+    @Mock
+    private Resources mResources;
+    @Mock
+    private AudioManager mAudioManager;
+    @Mock
+    private AudioService mAudioService;
+    @Mock
+    private DeviceRouteController.OnDeviceRouteChangedListener mOnDeviceRouteChangedListener;
+
+    @Captor
+    private ArgumentCaptor<IAudioRoutesObserver.Stub> mAudioRoutesObserverCaptor;
+
+    private AudioPoliciesDeviceRouteController mController;
+
+    private IAudioRoutesObserver.Stub mAudioRoutesObserver;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        when(mContext.getResources()).thenReturn(mResources);
+        when(mResources.getText(anyInt())).thenReturn(ROUTE_NAME_DEFAULT);
+
+        // Setting built-in speaker as default speaker.
+        AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo();
+        audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_SPEAKER;
+        when(mAudioService.startWatchingRoutes(mAudioRoutesObserverCaptor.capture()))
+                .thenReturn(audioRoutesInfo);
+
+        mController = new AudioPoliciesDeviceRouteController(
+                mContext, mAudioManager, mAudioService, mOnDeviceRouteChangedListener);
+
+        mAudioRoutesObserver = mAudioRoutesObserverCaptor.getValue();
+    }
+
+    @Test
+    public void getDeviceRoute_noSelectedRoutes_returnsDefaultDevice() {
+        MediaRoute2Info route2Info = mController.getDeviceRoute();
+
+        assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_DEFAULT);
+        assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_BUILTIN_SPEAKER);
+    }
+
+    @Test
+    public void getDeviceRoute_audioRouteHasChanged_returnsRouteFromAudioService() {
+        when(mResources.getText(R.string.default_audio_route_name_headphones))
+                .thenReturn(ROUTE_NAME_HEADPHONES);
+
+        AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo();
+        audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_HEADPHONES;
+        callAudioRoutesObserver(audioRoutesInfo);
+
+        MediaRoute2Info route2Info = mController.getDeviceRoute();
+        assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_HEADPHONES);
+        assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_WIRED_HEADPHONES);
+    }
+
+    @Test
+    public void getDeviceRoute_selectDevice_returnsSelectedRoute() {
+        when(mResources.getText(R.string.default_audio_route_name_dock_speakers))
+                .thenReturn(ROUTE_NAME_DOCK);
+
+        mController.selectRoute(MediaRoute2Info.TYPE_DOCK);
+
+        MediaRoute2Info route2Info = mController.getDeviceRoute();
+        assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_DOCK);
+        assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_DOCK);
+    }
+
+    @Test
+    public void getDeviceRoute_hasSelectedAndAudioServiceRoutes_returnsSelectedRoute() {
+        when(mResources.getText(R.string.default_audio_route_name_headphones))
+                .thenReturn(ROUTE_NAME_HEADPHONES);
+        when(mResources.getText(R.string.default_audio_route_name_dock_speakers))
+                .thenReturn(ROUTE_NAME_DOCK);
+
+        AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo();
+        audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_HEADPHONES;
+        callAudioRoutesObserver(audioRoutesInfo);
+
+        mController.selectRoute(MediaRoute2Info.TYPE_DOCK);
+
+        MediaRoute2Info route2Info = mController.getDeviceRoute();
+        assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_DOCK);
+        assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_DOCK);
+    }
+
+    @Test
+    public void getDeviceRoute_unselectRoute_returnsAudioServiceRoute() {
+        when(mResources.getText(R.string.default_audio_route_name_headphones))
+                .thenReturn(ROUTE_NAME_HEADPHONES);
+        when(mResources.getText(R.string.default_audio_route_name_dock_speakers))
+                .thenReturn(ROUTE_NAME_DOCK);
+
+        mController.selectRoute(MediaRoute2Info.TYPE_DOCK);
+
+        AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo();
+        audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_HEADPHONES;
+        callAudioRoutesObserver(audioRoutesInfo);
+
+        mController.selectRoute(null);
+
+        MediaRoute2Info route2Info = mController.getDeviceRoute();
+        assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_HEADPHONES);
+        assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_WIRED_HEADPHONES);
+    }
+
+    @Test
+    public void getDeviceRoute_selectRouteFails_returnsAudioServiceRoute() {
+        when(mResources.getText(R.string.default_audio_route_name_headphones))
+                .thenReturn(ROUTE_NAME_HEADPHONES);
+
+        AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo();
+        audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_HEADPHONES;
+        callAudioRoutesObserver(audioRoutesInfo);
+
+        mController.selectRoute(MediaRoute2Info.TYPE_BLUETOOTH_A2DP);
+
+        MediaRoute2Info route2Info = mController.getDeviceRoute();
+        assertThat(route2Info.getName()).isEqualTo(ROUTE_NAME_HEADPHONES);
+        assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_WIRED_HEADPHONES);
+    }
+
+    @Test
+    public void selectRoute_selectWiredRoute_returnsTrue() {
+        assertThat(mController.selectRoute(MediaRoute2Info.TYPE_HDMI)).isTrue();
+    }
+
+    @Test
+    public void selectRoute_selectBluetoothRoute_returnsFalse() {
+        assertThat(mController.selectRoute(MediaRoute2Info.TYPE_BLUETOOTH_A2DP)).isFalse();
+    }
+
+    @Test
+    public void selectRoute_unselectRoute_returnsTrue() {
+        assertThat(mController.selectRoute(null)).isTrue();
+    }
+
+    @Test
+    public void updateVolume_noSelectedRoute_deviceRouteVolumeChanged() {
+        when(mResources.getText(R.string.default_audio_route_name_headphones))
+                .thenReturn(ROUTE_NAME_HEADPHONES);
+
+        AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo();
+        audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_HEADPHONES;
+        callAudioRoutesObserver(audioRoutesInfo);
+
+        mController.updateVolume(VOLUME_SAMPLE_1);
+
+        MediaRoute2Info route2Info = mController.getDeviceRoute();
+        assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_WIRED_HEADPHONES);
+        assertThat(route2Info.getVolume()).isEqualTo(VOLUME_SAMPLE_1);
+    }
+
+    @Test
+    public void updateVolume_connectSelectedRouteLater_selectedRouteVolumeChanged() {
+        when(mResources.getText(R.string.default_audio_route_name_headphones))
+                .thenReturn(ROUTE_NAME_HEADPHONES);
+        when(mResources.getText(R.string.default_audio_route_name_dock_speakers))
+                .thenReturn(ROUTE_NAME_DOCK);
+
+        AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo();
+        audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_HEADPHONES;
+        callAudioRoutesObserver(audioRoutesInfo);
+
+        mController.updateVolume(VOLUME_SAMPLE_1);
+
+        mController.selectRoute(MediaRoute2Info.TYPE_DOCK);
+
+        MediaRoute2Info route2Info = mController.getDeviceRoute();
+        assertThat(route2Info.getType()).isEqualTo(MediaRoute2Info.TYPE_DOCK);
+        assertThat(route2Info.getVolume()).isEqualTo(VOLUME_SAMPLE_1);
+    }
+
+    /**
+     * Simulates {@link IAudioRoutesObserver.Stub#dispatchAudioRoutesChanged(AudioRoutesInfo)}
+     * from {@link AudioService}. This happens when there is a wired route change,
+     * like a wired headset being connected.
+     *
+     * @param audioRoutesInfo updated state of connected wired device
+     */
+    private void callAudioRoutesObserver(AudioRoutesInfo audioRoutesInfo) {
+        try {
+            // this is a captured observer implementation
+            // from WiredRoutesController's AudioService#startWatchingRoutes call
+            mAudioRoutesObserver.dispatchAudioRoutesChanged(audioRoutesInfo);
+        } catch (RemoteException exception) {
+            // Should not happen since the object is mocked.
+            assertWithMessage("An unexpected RemoteException happened.").fail();
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java b/services/tests/servicestests/src/com/android/server/media/LegacyDeviceRouteControllerTest.java
similarity index 96%
rename from services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java
rename to services/tests/servicestests/src/com/android/server/media/LegacyDeviceRouteControllerTest.java
index 24ed42c..24e4851 100644
--- a/services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/LegacyDeviceRouteControllerTest.java
@@ -50,7 +50,7 @@
 import java.util.Collection;
 
 @RunWith(Enclosed.class)
-public class DeviceRouteControllerTest {
+public class LegacyDeviceRouteControllerTest {
 
     private static final String DEFAULT_ROUTE_NAME = "default_route";
     private static final String DEFAULT_HEADPHONES_NAME = "headphone";
@@ -97,7 +97,7 @@
             // Default route should be initialized even when AudioService returns null.
             when(mAudioService.startWatchingRoutes(any())).thenReturn(null);
 
-            DeviceRouteController deviceRouteController = new DeviceRouteController(
+            LegacyDeviceRouteController deviceRouteController = new LegacyDeviceRouteController(
                     mContext,
                     mAudioManager,
                     mAudioService,
@@ -122,7 +122,7 @@
             AudioRoutesInfo fakeBluetoothAudioRoute = createFakeBluetoothAudioRoute();
             when(mAudioService.startWatchingRoutes(any())).thenReturn(fakeBluetoothAudioRoute);
 
-            DeviceRouteController deviceRouteController = new DeviceRouteController(
+            LegacyDeviceRouteController deviceRouteController = new LegacyDeviceRouteController(
                     mContext,
                     mAudioManager,
                     mAudioService,
@@ -236,7 +236,7 @@
             when(mResources.getText(mExpectedRouteNameResource))
                     .thenReturn(mExpectedRouteNameValue);
 
-            DeviceRouteController deviceRouteController = new DeviceRouteController(
+            LegacyDeviceRouteController deviceRouteController = new LegacyDeviceRouteController(
                     mContext,
                     mAudioManager,
                     mAudioService,
@@ -269,7 +269,7 @@
         @Captor
         private ArgumentCaptor<IAudioRoutesObserver.Stub> mAudioRoutesObserverCaptor;
 
-        private DeviceRouteController mDeviceRouteController;
+        private LegacyDeviceRouteController mDeviceRouteController;
         private IAudioRoutesObserver.Stub mAudioRoutesObserver;
 
         @Before
@@ -287,7 +287,7 @@
             when(mAudioService.startWatchingRoutes(mAudioRoutesObserverCaptor.capture()))
                     .thenReturn(audioRoutesInfo);
 
-            mDeviceRouteController = new DeviceRouteController(
+            mDeviceRouteController = new LegacyDeviceRouteController(
                     mContext,
                     mAudioManager,
                     mAudioService,
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java b/services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java
index 34e45c2..178670e 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java
@@ -18,6 +18,7 @@
 
 import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_ALARM;
 import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_UNKNOWN;
+import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_WIFI;
 
 import static com.android.server.power.stats.CpuWakeupStats.WAKEUP_REASON_HALF_WINDOW_MS;
 import static com.android.server.power.stats.CpuWakeupStats.WAKEUP_RETENTION_MS;
@@ -45,6 +46,7 @@
 @RunWith(AndroidJUnit4.class)
 public class CpuWakeupStatsTest {
     private static final String KERNEL_REASON_ALARM_IRQ = "120 test.alarm.device";
+    private static final String KERNEL_REASON_WIFI_IRQ = "120 test.wifi.device";
     private static final String KERNEL_REASON_UNKNOWN_IRQ = "140 test.unknown.device";
     private static final String KERNEL_REASON_UNKNOWN = "free-form-reason test.alarm.device";
     private static final String KERNEL_REASON_UNSUPPORTED = "-1 test.alarm.device";
@@ -68,22 +70,23 @@
         final Set<Long> timestamps = new HashSet<>();
         final long firstWakeup = 453192;
 
-        obj.noteWakeupTimeAndReason(firstWakeup, 32, "unused");
+        obj.noteWakeupTimeAndReason(firstWakeup, 32, KERNEL_REASON_UNKNOWN_IRQ);
         timestamps.add(firstWakeup);
         for (int i = 1; i < 1000; i++) {
             final long delta = mRandom.nextLong(WAKEUP_RETENTION_MS);
             if (timestamps.add(firstWakeup + delta)) {
-                obj.noteWakeupTimeAndReason(firstWakeup + delta, i, "unused");
+                obj.noteWakeupTimeAndReason(firstWakeup + delta, i, KERNEL_REASON_UNKNOWN_IRQ);
             }
         }
         assertThat(obj.mWakeupEvents.size()).isEqualTo(timestamps.size());
 
-        obj.noteWakeupTimeAndReason(firstWakeup + WAKEUP_RETENTION_MS + 1242, 231, "unused");
+        obj.noteWakeupTimeAndReason(firstWakeup + WAKEUP_RETENTION_MS + 1242, 231,
+                KERNEL_REASON_UNKNOWN_IRQ);
         assertThat(obj.mWakeupEvents.size()).isEqualTo(timestamps.size());
 
         for (int i = 0; i < 100; i++) {
             final long now = mRandom.nextLong(WAKEUP_RETENTION_MS + 1, 100 * WAKEUP_RETENTION_MS);
-            obj.noteWakeupTimeAndReason(now, i, "unused");
+            obj.noteWakeupTimeAndReason(now, i, KERNEL_REASON_UNKNOWN_IRQ);
             assertThat(obj.mWakeupEvents.closestIndexOnOrBefore(now - WAKEUP_RETENTION_MS))
                     .isLessThan(0);
         }
@@ -111,17 +114,45 @@
         assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_1)).isEqualTo(false);
         assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_2)).isEqualTo(false);
         assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_3)).isEqualTo(true);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_4)).isEqualTo(false);
         assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_5)).isEqualTo(true);
     }
 
     @Test
-    public void alarmIrqAttributionCombined() {
+    public void wifiIrqAttributionSolo() {
+        final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
+        final long wakeupTime = 12423121;
+
+        obj.noteWakeupTimeAndReason(wakeupTime, 1, KERNEL_REASON_WIFI_IRQ);
+
+        // Outside the window, so should be ignored.
+        obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI,
+                wakeupTime - WAKEUP_REASON_HALF_WINDOW_MS - 1, TEST_UID_1);
+        obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI,
+                wakeupTime + WAKEUP_REASON_HALF_WINDOW_MS + 1, TEST_UID_2);
+        // Should be attributed
+        obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI, wakeupTime + 3, TEST_UID_4, TEST_UID_5);
+
+        final SparseArray<SparseBooleanArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
+        assertThat(attribution).isNotNull();
+        assertThat(attribution.size()).isEqualTo(1);
+        assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_WIFI)).isTrue();
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_1)).isEqualTo(false);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_2)).isEqualTo(false);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_3)).isEqualTo(false);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_4)).isEqualTo(true);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_5)).isEqualTo(true);
+    }
+
+    @Test
+    public void alarmAndWifiIrqAttribution() {
         final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
         final long wakeupTime = 92123210;
 
         obj.noteWakeupTimeAndReason(wakeupTime, 4,
-                KERNEL_REASON_UNKNOWN_IRQ + ":" + KERNEL_REASON_ALARM_IRQ);
+                KERNEL_REASON_WIFI_IRQ + ":" + KERNEL_REASON_ALARM_IRQ);
 
+        // Alarm activity
         // Outside the window, so should be ignored.
         obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM,
                 wakeupTime - WAKEUP_REASON_HALF_WINDOW_MS - 1, TEST_UID_1);
@@ -132,16 +163,34 @@
         obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime - 3, TEST_UID_4,
                 TEST_UID_5);
 
+        // Wifi activity
+        // Outside the window, so should be ignored.
+        obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI,
+                wakeupTime - WAKEUP_REASON_HALF_WINDOW_MS - 1, TEST_UID_4);
+        obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI,
+                wakeupTime + WAKEUP_REASON_HALF_WINDOW_MS + 1, TEST_UID_3);
+        // Should be attributed
+        obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI, wakeupTime + 2, TEST_UID_1);
+        obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI, wakeupTime - 1, TEST_UID_2,
+                TEST_UID_5);
+
         final SparseArray<SparseBooleanArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
         assertThat(attribution).isNotNull();
         assertThat(attribution.size()).isEqualTo(2);
+
         assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_ALARM)).isTrue();
         assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_1)).isEqualTo(false);
         assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_2)).isEqualTo(false);
         assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_3)).isEqualTo(true);
         assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_4)).isEqualTo(true);
         assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_5)).isEqualTo(true);
-        assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_UNKNOWN)).isTrue();
+
+        assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_WIFI)).isTrue();
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_1)).isEqualTo(true);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_2)).isEqualTo(true);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_3)).isEqualTo(false);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_4)).isEqualTo(false);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_5)).isEqualTo(true);
     }
 
     @Test
@@ -151,9 +200,11 @@
 
         obj.noteWakeupTimeAndReason(wakeupTime, 24, KERNEL_REASON_UNKNOWN_IRQ);
 
+        assertThat(obj.mWakeupEvents.size()).isEqualTo(1);
+
         // Unrelated subsystems, should not be attributed
         obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime + 5, TEST_UID_3);
-        obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime - 3, TEST_UID_4,
+        obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI, wakeupTime - 3, TEST_UID_4,
                 TEST_UID_5);
 
         final SparseArray<SparseBooleanArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
@@ -165,42 +216,48 @@
     }
 
     @Test
-    public void unknownAttribution() {
+    public void unknownWakeupIgnored() {
         final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
         final long wakeupTime = 72123210;
 
         obj.noteWakeupTimeAndReason(wakeupTime, 34, KERNEL_REASON_UNKNOWN);
 
-        // Should be ignored as this type of wakeup is unsupported.
+        // Should be ignored as this type of wakeup is not known.
+        assertThat(obj.mWakeupEvents.size()).isEqualTo(0);
+
         obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime + 5, TEST_UID_3);
         obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime - 3, TEST_UID_4);
 
-        // There should be nothing in the attribution map.
+        // Any nearby activity should not end up in the attribution map.
         assertThat(obj.mWakeupAttribution.size()).isEqualTo(0);
     }
 
     @Test
-    public void unsupportedAttribution() {
+    public void unsupportedWakeupIgnored() {
         final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
 
         long wakeupTime = 970934;
         obj.noteWakeupTimeAndReason(wakeupTime, 34, KERNEL_REASON_UNSUPPORTED);
 
         // Should be ignored as this type of wakeup is unsupported.
+        assertThat(obj.mWakeupEvents.size()).isEqualTo(0);
+
         obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime + 5, TEST_UID_3);
         obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime - 3, TEST_UID_4);
 
-        // There should be nothing in the attribution map.
+        // Any nearby activity should not end up in the attribution map.
         assertThat(obj.mWakeupAttribution.size()).isEqualTo(0);
 
         wakeupTime = 883124;
         obj.noteWakeupTimeAndReason(wakeupTime, 3, KERNEL_REASON_ABORT);
 
         // Should be ignored as this type of wakeup is unsupported.
-        obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime + 2, TEST_UID_1, TEST_UID_4);
-        obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime - 5, TEST_UID_3);
+        assertThat(obj.mWakeupEvents.size()).isEqualTo(0);
 
-        // There should be nothing in the attribution map.
+        obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI, wakeupTime + 2, TEST_UID_1, TEST_UID_4);
+        obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI, wakeupTime - 5, TEST_UID_3);
+
+        // Any nearby activity should not end up in the attribution map.
         assertThat(obj.mWakeupAttribution.size()).isEqualTo(0);
     }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index 6f37e60..ce07621 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -24,6 +24,8 @@
 import static com.android.server.notification.ManagedServices.APPROVAL_BY_COMPONENT;
 import static com.android.server.notification.ManagedServices.APPROVAL_BY_PACKAGE;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertNotNull;
@@ -1201,28 +1203,11 @@
                     mIpm, approvalLevel);
             loadXml(service);
 
-            List<String> allowedPackagesForUser0 = new ArrayList<>();
-            allowedPackagesForUser0.add("this.is.a.package.name");
-            allowedPackagesForUser0.add("another.package");
-            allowedPackagesForUser0.add("secondary");
-
-            List<String> actual = service.getAllowedPackages(0);
-            assertEquals(3, actual.size());
-            for (String pkg : allowedPackagesForUser0) {
-                assertTrue(actual.contains(pkg));
-            }
-
-            List<String> allowedPackagesForUser10 = new ArrayList<>();
-            allowedPackagesForUser10.add("this.is.another.package");
-            allowedPackagesForUser10.add("package");
-            allowedPackagesForUser10.add("this.is.another.package");
-            allowedPackagesForUser10.add("component");
-
-            actual = service.getAllowedPackages(10);
-            assertEquals(4, actual.size());
-            for (String pkg : allowedPackagesForUser10) {
-                assertTrue(actual.contains(pkg));
-            }
+            assertThat(service.getAllowedPackages(0)).containsExactly("this.is.a.package.name",
+                    "another.package", "secondary");
+            assertThat(service.getAllowedPackages(10)).containsExactly("this.is.another.package",
+                    "package", "this.is.another.package", "component");
+            assertThat(service.getAllowedPackages(999)).isEmpty();
         }
     }
 
@@ -1263,31 +1248,6 @@
     }
 
     @Test
-    public void testGetAllowedPackages() throws Exception {
-        ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
-                mIpm, APPROVAL_BY_COMPONENT);
-        loadXml(service);
-        service.mApprovalLevel = APPROVAL_BY_PACKAGE;
-        loadXml(service);
-
-        List<String> allowedPackages = new ArrayList<>();
-        allowedPackages.add("this.is.a.package.name");
-        allowedPackages.add("another.package");
-        allowedPackages.add("secondary");
-        allowedPackages.add("this.is.another.package");
-        allowedPackages.add("package");
-        allowedPackages.add("component");
-        allowedPackages.add("bananas!");
-        allowedPackages.add("non.user.set.package");
-
-        Set<String> actual = service.getAllowedPackages();
-        assertEquals(allowedPackages.size(), actual.size());
-        for (String pkg : allowedPackages) {
-            assertTrue(actual.contains(pkg));
-        }
-    }
-
-    @Test
     public void testOnUserRemoved() throws Exception {
         for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
             ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index f08d0f5..354420f 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -29,6 +29,7 @@
 import static android.app.Notification.FLAG_NO_CLEAR;
 import static android.app.Notification.FLAG_ONGOING_EVENT;
 import static android.app.NotificationChannel.USER_LOCKED_ALLOW_BUBBLE;
+import static android.app.NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED;
 import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
 import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
 import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED;
@@ -50,7 +51,6 @@
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
-
 import static android.app.PendingIntent.FLAG_IMMUTABLE;
 import static android.app.PendingIntent.FLAG_MUTABLE;
 import static android.app.PendingIntent.FLAG_ONE_SHOT;
@@ -237,6 +237,7 @@
 import com.android.server.wm.ActivityTaskManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
 
+import com.google.android.collect.Lists;
 import com.google.common.collect.ImmutableList;
 
 import org.junit.After;
@@ -245,10 +246,13 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatcher;
+import org.mockito.ArgumentMatchers;
 import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 
 import java.io.BufferedInputStream;
@@ -440,6 +444,7 @@
         mContext.addMockSystemService(Context.ALARM_SERVICE, mAlarmManager);
         mContext.addMockSystemService(NotificationManager.class, mMockNm);
 
+        doNothing().when(mContext).sendBroadcastAsUser(any(), any());
         doNothing().when(mContext).sendBroadcastAsUser(any(), any(), any());
 
         setDpmAppOppsExemptFromDismissal(false);
@@ -7828,6 +7833,75 @@
     }
 
     @Test
+    public void onZenModeChanged_sendsBroadcasts() throws Exception {
+        when(mAmi.getCurrentUserId()).thenReturn(100);
+        when(mUmInternal.getProfileIds(eq(100), anyBoolean())).thenReturn(new int[]{100, 101, 102});
+        when(mConditionProviders.getAllowedPackages(anyInt())).then(new Answer<List<String>>() {
+            @Override
+            public List<String> answer(InvocationOnMock invocation) {
+                int userId = invocation.getArgument(0);
+                switch (userId) {
+                    case 100:
+                        return Lists.newArrayList("a", "b", "c");
+                    case 101:
+                        return Lists.newArrayList();
+                    case 102:
+                        return Lists.newArrayList("b");
+                    default:
+                        throw new IllegalArgumentException(
+                                "Why would you ask for packages of userId " + userId + "?");
+                }
+            }
+        });
+
+        mService.getBinderService().setZenMode(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS, null,
+                "testing!");
+        waitForIdle();
+
+        InOrder inOrder = inOrder(mContext);
+        // Verify broadcasts for registered receivers
+        inOrder.verify(mContext).sendBroadcastAsUser(eqIntent(
+                new Intent(ACTION_INTERRUPTION_FILTER_CHANGED).setFlags(
+                        Intent.FLAG_RECEIVER_REGISTERED_ONLY)), eq(UserHandle.of(100)), eq(null));
+        inOrder.verify(mContext).sendBroadcastAsUser(eqIntent(
+                new Intent(ACTION_INTERRUPTION_FILTER_CHANGED).setFlags(
+                        Intent.FLAG_RECEIVER_REGISTERED_ONLY)), eq(UserHandle.of(101)), eq(null));
+        inOrder.verify(mContext).sendBroadcastAsUser(eqIntent(
+                new Intent(ACTION_INTERRUPTION_FILTER_CHANGED).setFlags(
+                        Intent.FLAG_RECEIVER_REGISTERED_ONLY)), eq(UserHandle.of(102)), eq(null));
+
+        // Verify broadcast for packages that manage DND.
+        inOrder.verify(mContext).sendBroadcastAsUser(eqIntent(new Intent(
+                ACTION_INTERRUPTION_FILTER_CHANGED).setPackage("a").setFlags(
+                Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT)), eq(UserHandle.of(100)));
+        inOrder.verify(mContext).sendBroadcastAsUser(eqIntent(new Intent(
+                ACTION_INTERRUPTION_FILTER_CHANGED).setPackage("b").setFlags(
+                Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT)), eq(UserHandle.of(100)));
+        inOrder.verify(mContext).sendBroadcastAsUser(eqIntent(new Intent(
+                ACTION_INTERRUPTION_FILTER_CHANGED).setPackage("c").setFlags(
+                Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT)), eq(UserHandle.of(100)));
+        inOrder.verify(mContext).sendBroadcastAsUser(eqIntent(new Intent(
+                ACTION_INTERRUPTION_FILTER_CHANGED).setPackage("b").setFlags(
+                Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT)), eq(UserHandle.of(102)));
+    }
+
+    private static Intent eqIntent(Intent wanted) {
+        return ArgumentMatchers.argThat(
+                new ArgumentMatcher<Intent>() {
+                    @Override
+                    public boolean matches(Intent argument) {
+                        return wanted.filterEquals(argument)
+                                && wanted.getFlags() == argument.getFlags();
+                    }
+
+                    @Override
+                    public String toString() {
+                        return wanted.toString();
+                    }
+                });
+    }
+
+    @Test
     public void testAreNotificationsEnabledForPackage() throws Exception {
         mBinderService.areNotificationsEnabledForPackage(mContext.getPackageName(),
                 mUid);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 6661e6a..49f215a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2897,7 +2897,7 @@
 
         // Make the top one invisible, and try transferring the starting window from the top to the
         // bottom one.
-        activityTop.setVisibility(false, false);
+        activityTop.setVisibility(false);
         activityBottom.transferStartingWindowFromHiddenAboveTokenIfNeeded();
         waitUntilHandlersIdle();
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
index 4d71b30..6d13124 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
@@ -165,7 +165,7 @@
         assertTrue(mTask.isInChangeTransition());
 
         // Changing visibility should cancel the change transition and become closing
-        mActivity.setVisibility(false, false);
+        mActivity.setVisibility(false);
         assertEquals(0, mDisplayContent.mChangingContainers.size());
         assertFalse(mTask.isInChangeTransition());
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index 6b814e6..59cc4f5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -295,8 +295,8 @@
         dc2.prepareAppTransition(TRANSIT_CLOSE);
         // One activity window is visible for resuming & the other activity window is invisible
         // for finishing in different display.
-        activity1.setVisibility(true, false);
-        activity2.setVisibility(false, false);
+        activity1.setVisibility(true);
+        activity2.setVisibility(false);
 
         // Make sure each display is in animating stage.
         assertTrue(dc1.mOpeningApps.size() > 0);
@@ -365,7 +365,7 @@
         dc.prepareAppTransition(TRANSIT_CLOSE);
         assertTrue(dc.mAppTransition.containsTransitRequest(TRANSIT_CLOSE));
         dc.mAppTransition.overridePendingAppTransitionRemote(adapter);
-        exitingActivity.setVisibility(false, false);
+        exitingActivity.setVisibility(false);
         assertTrue(dc.mClosingApps.size() > 0);
 
         // Make sure window is in animating stage before freeze, and cancel after freeze.
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index aaeae23..3379beb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1620,7 +1620,6 @@
 
         // If the rotated activity requests to show IME, the IME window should use the
         // transformation from activity to lay out in the same orientation.
-        mDisplayContent.setImeLayeringTarget(mAppWindow);
         LocalServices.getService(WindowManagerInternal.class).onToggleImeRequested(true /* show */,
                 app.token, app.token, mDisplayContent.mDisplayId);
         assertTrue(asyncRotationController.isTargetToken(mImeWindow.mToken));
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
index df3af7d..49d8da1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -351,8 +351,8 @@
         final TaskFragment taskFragment1 = createTaskFragmentWithEmbeddedActivity(task, mOrganizer);
         final ActivityRecord activity0 = taskFragment0.getTopMostActivity();
         final ActivityRecord activity1 = taskFragment1.getTopMostActivity();
-        activity0.setVisibility(true /* visible */, false /* deferHidingClient */);
-        activity1.setVisibility(true /* visible */, false /* deferHidingClient */);
+        activity0.setVisibility(true);
+        activity1.setVisibility(true);
         spyOn(mAtm.mTaskFragmentOrganizerController);
 
         // Move activity to pinned.
diff --git a/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
index 2fccb88a..75a8dd8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
@@ -92,11 +92,11 @@
 
     @Test
     public void testRemoveFinishingInvisibleActivityFromUnknown() {
-        final ActivityRecord activity = createNonAttachedActivityRecord(mDisplayContent);
+        final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
         mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(activity);
         activity.finishing = true;
         activity.setVisibleRequested(true);
-        activity.setVisibility(false, false);
+        activity.setVisibility(false);
         assertTrue(mDisplayContent.mUnknownAppVisibilityController.allResolved());
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 3aee2cd..33067df 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -914,15 +914,15 @@
 
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app", uid);
         app.mActivityRecord.setVisible(false);
-        app.mActivityRecord.setVisibility(false /* visible */, false /* deferHidingClient */);
+        app.mActivityRecord.setVisibility(false);
         assertFalse(mAtm.hasActiveVisibleWindow(uid));
 
-        app.mActivityRecord.setVisibility(true /* visible */, false /* deferHidingClient */);
+        app.mActivityRecord.setVisibility(true);
         assertTrue(mAtm.hasActiveVisibleWindow(uid));
 
         // Make the activity invisible and add a visible toast. The uid should have no active
         // visible window because toast can be misused by legacy app to bypass background check.
-        app.mActivityRecord.setVisibility(false /* visible */, false /* deferHidingClient */);
+        app.mActivityRecord.setVisibility(false);
         final WindowState overlay = createWindow(null, TYPE_APPLICATION_OVERLAY, "overlay", uid);
         final WindowState toast = createWindow(null, TYPE_TOAST, app.mToken, "toast", uid);
         toast.onSurfaceShownChanged(true);
@@ -1157,12 +1157,12 @@
     public void testRequestedVisibility() {
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
         app.mActivityRecord.setVisible(false);
-        app.mActivityRecord.setVisibility(false /* visible */, false /* deferHidingClient */);
+        app.mActivityRecord.setVisibility(false);
         assertFalse(app.isVisibleRequested());
 
         // It doesn't have a surface yet, but should still be visible requested.
         app.setHasSurface(false);
-        app.mActivityRecord.setVisibility(true /* visible */, false /* deferHidingClient */);
+        app.mActivityRecord.setVisibility(true);
 
         assertFalse(app.isVisible());
         assertTrue(app.isVisibleRequested());
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index 9dc4bf0..9c3460c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -317,7 +317,8 @@
             assertion.then().isVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true)
         }
         if (ignoreSplashscreen) {
-            assertion.then().isSplashScreenVisibleFor(newLayer, isOptional = true)
+            assertion.then().isSplashScreenVisibleFor(
+                    ComponentNameMatcher(newLayer.packageName, className = ""), isOptional = true)
         }
 
         assertion.then().isVisible(newLayer)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt
index 6005a81..786bb32 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt
@@ -16,9 +16,10 @@
 
 package com.android.server.wm.flicker.launch
 
+import android.os.SystemClock
 import android.platform.test.annotations.Postsubmit
 import android.tools.device.apphelpers.CameraAppHelper
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.device.apphelpers.StandardAppHelper
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
 import android.tools.device.flicker.legacy.FlickerBuilder
 import android.tools.device.flicker.legacy.FlickerTest
@@ -54,13 +55,14 @@
  * ```
  */
 @RequiresDevice
-@FlickerServiceCompatible
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 class OpenCameraOnDoubleClickPowerButton(flicker: FlickerTest) :
     OpenAppFromLauncherTransition(flicker) {
     private val cameraApp = CameraAppHelper(instrumentation)
+    override val testApp: StandardAppHelper
+        get() = cameraApp
 
     override val transition: FlickerBuilder.() -> Unit
         get() = {
@@ -70,6 +72,7 @@
             }
             transitions {
                 device.pressKeyCode(KeyEvent.KEYCODE_POWER)
+                SystemClock.sleep(100)
                 device.pressKeyCode(KeyEvent.KEYCODE_POWER)
                 wmHelper.StateSyncBuilder().withWindowSurfaceAppeared(cameraApp).waitForAndVerify()
             }
diff --git a/tests/Input/src/com/android/test/input/MotionPredictorTest.kt b/tests/Input/src/com/android/test/input/MotionPredictorTest.kt
index 8b1b06f..24a5671 100644
--- a/tests/Input/src/com/android/test/input/MotionPredictorTest.kt
+++ b/tests/Input/src/com/android/test/input/MotionPredictorTest.kt
@@ -124,14 +124,12 @@
         predictor.record(moveEvent)
 
         val predicted = predictor.predict(Duration.ofMillis(8).toNanos())
-        assertEquals(1, predicted.size)
-        val event = predicted[0]
-        assertNotNull(event)
+        assertNotNull(predicted)
 
         // Prediction will happen for t=12 (since it is the next input interval after the requested
         // time, 8, plus the model offset, 1).
-        assertEquals(12, event.eventTime)
-        assertEquals(30f, event.x, /*delta=*/5f)
-        assertEquals(60f, event.y, /*delta=*/15f)
+        assertEquals(12, predicted!!.eventTime)
+        assertEquals(30f, predicted.x, /*delta=*/5f)
+        assertEquals(60f, predicted.y, /*delta=*/15f)
     }
 }
diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java
index 573b3b69..d2708ad 100644
--- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java
+++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java
@@ -42,6 +42,7 @@
 import androidx.test.uiautomator.By;
 import androidx.test.uiautomator.BySelector;
 import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject2;
 import androidx.test.uiautomator.Until;
 
 import org.junit.After;
@@ -74,7 +75,7 @@
 
     // This is for AOSP System UI for phones. When testing customized System UI, please modify here.
     private static final BySelector REPLY_SEND_BUTTON_SELECTOR =
-            By.res("com.android.systemui", "remote_input_send");
+            By.res("com.android.systemui", "remote_input_send").enabled(true);
 
     @Rule
     public UnlockScreenRule mUnlockScreenRule = new UnlockScreenRule();
@@ -119,7 +120,15 @@
         mUiDevice.pressKeyCode(KeyEvent.KEYCODE_A);
         mUiDevice.pressKeyCode(KeyEvent.KEYCODE_B);
         mUiDevice.pressKeyCode(KeyEvent.KEYCODE_C);
-        mUiDevice.wait(Until.findObject(REPLY_SEND_BUTTON_SELECTOR.enabled(true)), TIMEOUT).click();
+        UiObject2 sendButton = mUiDevice.wait(
+                Until.findObject(REPLY_SEND_BUTTON_SELECTOR), TIMEOUT);
+        if (sendButton == null) {
+            // If the screen is too small, sendButton may be hidden by IME.
+            // Dismiss IME and try again.
+            mUiDevice.pressBack();
+            sendButton = mUiDevice.wait(Until.findObject(REPLY_SEND_BUTTON_SELECTOR), TIMEOUT);
+        }
+        sendButton.click();
         // Verify that IME is gone.
         assertThat(mUiDevice.wait(Until.gone(By.pkg(getImePackage(mContext))), TIMEOUT)).isTrue();
     }
diff --git a/tests/MotionPrediction/src/test/motionprediction/DrawingView.kt b/tests/MotionPrediction/src/test/motionprediction/DrawingView.kt
index f529bf7..229d0c8 100644
--- a/tests/MotionPrediction/src/test/motionprediction/DrawingView.kt
+++ b/tests/MotionPrediction/src/test/motionprediction/DrawingView.kt
@@ -97,8 +97,8 @@
         }
 
         // Draw predictions. Convert to nanos and hardcode to +20ms into the future
-        val predictionList = predictor.predict(eventTime * 1000000 + 20000000)
-        for (prediction in predictionList) {
+        val prediction = predictor.predict(eventTime * 1000000 + 20000000)
+        if (prediction != null) {
             val realEvents = events.get(prediction.deviceId)!!
             drawLine(canvas, realEvents[realEvents.size - 1], prediction, predictionPaint)
         }