Merge "Decouple Fgs logic from UsageStats reporting logic" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 3f834fa..a5e695a 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -910,6 +910,8 @@
 aconfig_declarations {
     name: "android.service.notification.flags-aconfig",
     package: "android.service.notification",
+    exportable: true,
+    container: "system",
     srcs: ["core/java/android/service/notification/flags.aconfig"],
 }
 
@@ -919,6 +921,18 @@
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
 
+java_aconfig_library {
+    name: "android.service.notification.flags-aconfig-export-java",
+    aconfig_declarations: "android.service.notification.flags-aconfig",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+    mode: "exported",
+    min_sdk_version: "30",
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.extservices",
+    ],
+}
+
 // Smartspace
 aconfig_declarations {
     name: "android.app.smartspace.flags-aconfig",
@@ -985,6 +999,11 @@
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
 
+cc_aconfig_library {
+    name: "android.tracing.flags_c_lib",
+    aconfig_declarations: "android.tracing.flags-aconfig",
+}
+
 // App Widgets
 aconfig_declarations {
     name: "android.appwidget.flags-aconfig",
diff --git a/Android.bp b/Android.bp
index 870df5a..019bf6508 100644
--- a/Android.bp
+++ b/Android.bp
@@ -508,7 +508,6 @@
     lint: {
         baseline_filename: "lint-baseline.xml",
     },
-    // For jarjar repackaging
     jarjar_prefix: "com.android.internal.hidden_from_bootclasspath",
 }
 
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 4ec476e..48dcbe5 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1703,6 +1703,7 @@
     field public static final int SWITCHING_TYPE_WITHIN_GROUPS = 1; // 0x1
     field public static final int VIRTUAL_DISPLAY_FLAG_OWN_FOCUS = 16384; // 0x4000
     field public static final int VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS = 512; // 0x200
+    field public static final int VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH = 64; // 0x40
   }
 
 }
@@ -1751,6 +1752,10 @@
     field public static final int DEFAULT_POINTER_SPEED = 0; // 0x0
   }
 
+  public class VirtualKeyboard implements java.io.Closeable {
+    method public int getInputDeviceId();
+  }
+
 }
 
 package android.hardware.lights {
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 926e297..e6a2c07 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -237,7 +237,6 @@
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.org.conscrypt.TrustedCertificateStore;
 import com.android.server.am.MemInfoDumpProto;
-import com.android.window.flags.Flags;
 
 import dalvik.annotation.optimization.NeverCompile;
 import dalvik.system.AppSpecializationHooks;
@@ -3762,11 +3761,7 @@
         final ClientTransaction clientTransaction = ClientTransaction.obtain(mAppThread);
         final ActivityResultItem activityResultItem = ActivityResultItem.obtain(
                 activityToken, list);
-        if (Flags.bundleClientTransactionFlag()) {
-            clientTransaction.addTransactionItem(activityResultItem);
-        } else {
-            clientTransaction.addCallback(activityResultItem);
-        }
+        clientTransaction.addTransactionItem(activityResultItem);
         try {
             mAppThread.scheduleTransaction(clientTransaction);
         } catch (RemoteException e) {
@@ -4553,11 +4548,7 @@
         final PauseActivityItem pauseActivityItem = PauseActivityItem.obtain(r.token,
                 r.activity.isFinishing(), /* userLeaving */ true, r.activity.mConfigChangeFlags,
                 /* dontReport */ false, /* autoEnteringPip */ false);
-        if (Flags.bundleClientTransactionFlag()) {
-            transaction.addTransactionItem(pauseActivityItem);
-        } else {
-            transaction.setLifecycleStateRequest(pauseActivityItem);
-        }
+        transaction.addTransactionItem(pauseActivityItem);
         executeTransaction(transaction);
     }
 
@@ -4565,11 +4556,7 @@
         final ClientTransaction transaction = ClientTransaction.obtain(mAppThread);
         final ResumeActivityItem resumeActivityItem = ResumeActivityItem.obtain(r.token,
                 /* isForward */ false, /* shouldSendCompatFakeFocus */ false);
-        if (Flags.bundleClientTransactionFlag()) {
-            transaction.addTransactionItem(resumeActivityItem);
-        } else {
-            transaction.setLifecycleStateRequest(resumeActivityItem);
-        }
+        transaction.addTransactionItem(resumeActivityItem);
         executeTransaction(transaction);
     }
 
@@ -6189,13 +6176,8 @@
                 TransactionExecutorHelper.getLifecycleRequestForCurrentState(r);
         // Schedule the transaction.
         final ClientTransaction transaction = ClientTransaction.obtain(mAppThread);
-        if (Flags.bundleClientTransactionFlag()) {
-            transaction.addTransactionItem(activityRelaunchItem);
-            transaction.addTransactionItem(lifecycleRequest);
-        } else {
-            transaction.addCallback(activityRelaunchItem);
-            transaction.setLifecycleStateRequest(lifecycleRequest);
-        }
+        transaction.addTransactionItem(activityRelaunchItem);
+        transaction.addTransactionItem(lifecycleRequest);
         executeTransaction(transaction);
     }
 
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index d6e8ae3..26f85f7 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -80,6 +80,7 @@
 import android.os.Parcelable;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
@@ -3023,37 +3024,44 @@
      * @hide
      */
     public String loadHeaderAppName(Context context) {
-        CharSequence name = null;
-        // Check if there is a non-empty substitute app name and return that.
-        if (extras.containsKey(EXTRA_SUBSTITUTE_APP_NAME)) {
-            name = extras.getString(EXTRA_SUBSTITUTE_APP_NAME);
-            if (!TextUtils.isEmpty(name)) {
-                return name.toString();
-            }
-        }
-        // If not, try getting the app info from extras.
-        if (context == null) {
-            return null;
-        }
-        final PackageManager pm = context.getPackageManager();
-        if (TextUtils.isEmpty(name)) {
-            if (extras.containsKey(EXTRA_BUILDER_APPLICATION_INFO)) {
-                final ApplicationInfo info = extras.getParcelable(EXTRA_BUILDER_APPLICATION_INFO,
-                        ApplicationInfo.class);
-                if (info != null) {
-                    name = pm.getApplicationLabel(info);
+        Trace.beginSection("Notification#loadHeaderAppName");
+
+        try {
+            CharSequence name = null;
+            // Check if there is a non-empty substitute app name and return that.
+            if (extras.containsKey(EXTRA_SUBSTITUTE_APP_NAME)) {
+                name = extras.getString(EXTRA_SUBSTITUTE_APP_NAME);
+                if (!TextUtils.isEmpty(name)) {
+                    return name.toString();
                 }
             }
+            // If not, try getting the app info from extras.
+            if (context == null) {
+                return null;
+            }
+            final PackageManager pm = context.getPackageManager();
+            if (TextUtils.isEmpty(name)) {
+                if (extras.containsKey(EXTRA_BUILDER_APPLICATION_INFO)) {
+                    final ApplicationInfo info = extras.getParcelable(
+                            EXTRA_BUILDER_APPLICATION_INFO,
+                            ApplicationInfo.class);
+                    if (info != null) {
+                        name = pm.getApplicationLabel(info);
+                    }
+                }
+            }
+            // If that's still empty, use the one from the context directly.
+            if (TextUtils.isEmpty(name)) {
+                name = pm.getApplicationLabel(context.getApplicationInfo());
+            }
+            // If there's still nothing, ¯\_(ツ)_/¯
+            if (TextUtils.isEmpty(name)) {
+                return null;
+            }
+            return name.toString();
+        } finally {
+            Trace.endSection();
         }
-        // If that's still empty, use the one from the context directly.
-        if (TextUtils.isEmpty(name)) {
-            name = pm.getApplicationLabel(context.getApplicationInfo());
-        }
-        // If there's still nothing, ¯\_(ツ)_/¯
-        if (TextUtils.isEmpty(name)) {
-            return null;
-        }
-        return name.toString();
     }
 
     /**
@@ -6722,23 +6730,29 @@
          */
         @NonNull
         public static Notification.Builder recoverBuilder(Context context, Notification n) {
-            // Re-create notification context so we can access app resources.
-            ApplicationInfo applicationInfo = n.extras.getParcelable(
-                    EXTRA_BUILDER_APPLICATION_INFO, ApplicationInfo.class);
-            Context builderContext;
-            if (applicationInfo != null) {
-                try {
-                    builderContext = context.createApplicationContext(applicationInfo,
-                            Context.CONTEXT_RESTRICTED);
-                } catch (NameNotFoundException e) {
-                    Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found");
-                    builderContext = context;  // try with our context
-                }
-            } else {
-                builderContext = context; // try with given context
-            }
+            Trace.beginSection("Notification.Builder#recoverBuilder");
 
-            return new Builder(builderContext, n);
+            try {
+                // Re-create notification context so we can access app resources.
+                ApplicationInfo applicationInfo = n.extras.getParcelable(
+                        EXTRA_BUILDER_APPLICATION_INFO, ApplicationInfo.class);
+                Context builderContext;
+                if (applicationInfo != null) {
+                    try {
+                        builderContext = context.createApplicationContext(applicationInfo,
+                                Context.CONTEXT_RESTRICTED);
+                    } catch (NameNotFoundException e) {
+                        Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found");
+                        builderContext = context;  // try with our context
+                    }
+                } else {
+                    builderContext = context; // try with given context
+                }
+
+                return new Builder(builderContext, n);
+            } finally {
+                Trace.endSection();
+            }
         }
 
         /**
diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java
index 612d433..79696e0 100644
--- a/core/java/android/app/servertransaction/ClientTransaction.java
+++ b/core/java/android/app/servertransaction/ClientTransaction.java
@@ -29,6 +29,7 @@
 import android.os.RemoteException;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.window.flags.Flags;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -85,12 +86,15 @@
      * @param item A single message that can contain a client activity/window request/callback.
      */
     public void addTransactionItem(@NonNull ClientTransactionItem item) {
-        if (mTransactionItems == null) {
-            mTransactionItems = new ArrayList<>();
+        if (Flags.bundleClientTransactionFlag()) {
+            if (mTransactionItems == null) {
+                mTransactionItems = new ArrayList<>();
+            }
+            mTransactionItems.add(item);
         }
-        mTransactionItems.add(item);
 
         // TODO(b/324203798): cleanup after remove UnsupportedAppUsage
+        // Populate even if mTransactionItems is set to support the UnsupportedAppUsage.
         if (item.isActivityLifecycleItem()) {
             setLifecycleStateRequest((ActivityLifecycleItem) item);
         } else {
@@ -114,7 +118,7 @@
      */
     // TODO(b/324203798): cleanup after remove UnsupportedAppUsage
     @Deprecated
-    public void addCallback(@NonNull ClientTransactionItem activityCallback) {
+    private void addCallback(@NonNull ClientTransactionItem activityCallback) {
         if (mActivityCallbacks == null) {
             mActivityCallbacks = new ArrayList<>();
         }
@@ -169,7 +173,7 @@
      */
     // TODO(b/324203798): cleanup after remove UnsupportedAppUsage
     @Deprecated
-    public void setLifecycleStateRequest(@NonNull ActivityLifecycleItem stateRequest) {
+    private void setLifecycleStateRequest(@NonNull ActivityLifecycleItem stateRequest) {
         if (mLifecycleStateRequest != null) {
             return;
         }
diff --git a/core/java/android/app/servertransaction/ClientTransactionListenerController.java b/core/java/android/app/servertransaction/ClientTransactionListenerController.java
index 9f97f6f..1a8136e 100644
--- a/core/java/android/app/servertransaction/ClientTransactionListenerController.java
+++ b/core/java/android/app/servertransaction/ClientTransactionListenerController.java
@@ -23,7 +23,6 @@
 import android.annotation.NonNull;
 import android.app.ActivityThread;
 import android.hardware.display.DisplayManagerGlobal;
-import android.os.Process;
 
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -67,7 +66,7 @@
      * window configuration.
      */
     public void onDisplayChanged(int displayId) {
-        if (!isBundleClientTransactionFlagEnabled()) {
+        if (!bundleClientTransactionFlag()) {
             return;
         }
         if (ActivityThread.isSystem()) {
@@ -76,10 +75,4 @@
         }
         mDisplayManager.handleDisplayChangeFromWindowManager(displayId);
     }
-
-    /** Whether {@link #bundleClientTransactionFlag} feature flag is enabled. */
-    public boolean isBundleClientTransactionFlagEnabled() {
-        // Can't read flag from isolated process.
-        return !Process.isIsolated() && bundleClientTransactionFlag();
-    }
 }
diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java
index 406e00a..fa73c99 100644
--- a/core/java/android/app/servertransaction/TransactionExecutor.java
+++ b/core/java/android/app/servertransaction/TransactionExecutor.java
@@ -40,7 +40,6 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.os.IBinder;
-import android.os.Process;
 import android.os.Trace;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -218,8 +217,6 @@
         final boolean shouldTrackConfigUpdatedContext =
                 // No configuration change for local transaction.
                 !mTransactionHandler.isExecutingLocalTransaction()
-                        // Can't read flag from isolated process.
-                        && !Process.isIsolated()
                         && bundleClientTransactionFlag();
         final Context configUpdatedContext = shouldTrackConfigUpdatedContext
                 ? item.getContextToUpdate(mTransactionHandler)
diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig
index 662f9d2..24d6a5c 100644
--- a/core/java/android/companion/virtual/flags/flags.aconfig
+++ b/core/java/android/companion/virtual/flags/flags.aconfig
@@ -37,3 +37,10 @@
      bug: "324842215"
      is_fixed_read_only: true
 }
+
+flag {
+     namespace: "virtual_devices"
+     name: "camera_device_awareness"
+     description: "Enable device awareness in camera service"
+     bug: "305170199"
+}
diff --git a/core/java/android/hardware/display/BrightnessInfo.java b/core/java/android/hardware/display/BrightnessInfo.java
index 53a9a75..c091062 100644
--- a/core/java/android/hardware/display/BrightnessInfo.java
+++ b/core/java/android/hardware/display/BrightnessInfo.java
@@ -80,6 +80,11 @@
      */
     public static final int BRIGHTNESS_MAX_REASON_POWER_IC = 2;
 
+    /**
+     * Maximum brightness is restricted due to the Wear bedtime mode.
+     */
+    public static final int BRIGHTNESS_MAX_REASON_WEAR_BEDTIME_MODE = 3;
+
     /** Brightness */
     public final float brightness;
 
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 8f0e0c9..eb26a76 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -28,6 +28,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
@@ -367,6 +368,8 @@
      * @see #createVirtualDisplay
      * @hide
      */
+    @SuppressLint("UnflaggedApi") // @TestApi without associated feature.
+    @TestApi
     public static final int VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH = 1 << 6;
 
     /**
diff --git a/core/java/android/hardware/input/KeyboardLayoutPreviewDrawable.java b/core/java/android/hardware/input/KeyboardLayoutPreviewDrawable.java
index 1cc910c..e47a48d 100644
--- a/core/java/android/hardware/input/KeyboardLayoutPreviewDrawable.java
+++ b/core/java/android/hardware/input/KeyboardLayoutPreviewDrawable.java
@@ -48,14 +48,13 @@
     private static final int GRAVITY_RIGHT = 0x2;
     private static final int GRAVITY_TOP = 0x4;
     private static final int GRAVITY_BOTTOM = 0x8;
-    private static final int GRAVITY_CENTER =
-            GRAVITY_LEFT | GRAVITY_RIGHT | GRAVITY_TOP | GRAVITY_BOTTOM;
-    private static final int GRAVITY_CENTER_HORIZONTAL = GRAVITY_LEFT | GRAVITY_RIGHT;
+    private static final int TEXT_PADDING_IN_DP = 1;
     private static final int KEY_PADDING_IN_DP = 3;
     private static final int KEYBOARD_PADDING_IN_DP = 10;
     private static final int KEY_RADIUS_IN_DP = 5;
     private static final int KEYBOARD_RADIUS_IN_DP = 10;
-    private static final int GLYPH_TEXT_SIZE_IN_SP = 10;
+    private static final int MIN_GLYPH_TEXT_SIZE_IN_SP = 10;
+    private static final int MAX_GLYPH_TEXT_SIZE_IN_SP = 20;
 
     private final List<KeyDrawable> mKeyDrawables = new ArrayList<>();
 
@@ -107,6 +106,8 @@
         }
         int rowCount = keys.length;
         float keyHeight = (float) (height - rowCount * 2 * keyPadding) / rowCount;
+        // Based on key height calculate the max text size that can fit for typing keys
+        mResourceProvider.calculateBestTextSizeForKey(keyHeight);
         float isoEnterKeyLeft = 0;
         float isoEnterKeyTop = 0;
         float isoEnterWidthUnit = 0;
@@ -136,16 +137,19 @@
                 }
                 if (PhysicalKeyLayout.isSpecialKey(row[j])) {
                     mKeyDrawables.add(new TypingKey(null, keyRect, keyRadius,
+                            mResourceProvider.getTextPadding(),
                             mResourceProvider.getSpecialKeyPaint(),
                             mResourceProvider.getSpecialKeyPaint(),
                             mResourceProvider.getSpecialKeyPaint()));
                 } else if (PhysicalKeyLayout.isKeyPositionUnsure(row[j])) {
                     mKeyDrawables.add(new UnsureTypingKey(row[j].glyph(), keyRect,
-                            keyRadius, mResourceProvider.getTypingKeyPaint(),
+                            keyRadius, mResourceProvider.getTextPadding(),
+                            mResourceProvider.getTypingKeyPaint(),
                             mResourceProvider.getPrimaryGlyphPaint(),
                             mResourceProvider.getSecondaryGlyphPaint()));
                 } else {
                     mKeyDrawables.add(new TypingKey(row[j].glyph(), keyRect, keyRadius,
+                            mResourceProvider.getTextPadding(),
                             mResourceProvider.getTypingKeyPaint(),
                             mResourceProvider.getPrimaryGlyphPaint(),
                             mResourceProvider.getSecondaryGlyphPaint()));
@@ -192,15 +196,18 @@
 
         private final RectF mKeyRect;
         private final float mKeyRadius;
+        private final float mTextPadding;
         private final Paint mKeyPaint;
         private final Paint mBaseTextPaint;
         private final Paint mModifierTextPaint;
         private final List<GlyphDrawable> mGlyphDrawables = new ArrayList<>();
 
         private TypingKey(@Nullable PhysicalKeyLayout.KeyGlyph glyphData, RectF keyRect,
-                float keyRadius, Paint keyPaint, Paint baseTextPaint, Paint modifierTextPaint) {
+                float keyRadius, float textPadding, Paint keyPaint, Paint baseTextPaint,
+                Paint modifierTextPaint) {
             mKeyRect = keyRect;
             mKeyRadius = keyRadius;
+            mTextPadding = textPadding;
             mKeyPaint = keyPaint;
             mBaseTextPaint = baseTextPaint;
             mModifierTextPaint = modifierTextPaint;
@@ -219,20 +226,17 @@
             if (!glyphData.hasBaseText()) {
                 return;
             }
-            boolean isCenter = !glyphData.hasValidAltGrText() && !glyphData.hasValidAltShiftText();
             mGlyphDrawables.add(new GlyphDrawable(glyphData.getBaseText(), new RectF(),
-                    GRAVITY_BOTTOM | (isCenter ? GRAVITY_CENTER_HORIZONTAL : GRAVITY_LEFT),
-                    mBaseTextPaint));
+                    GRAVITY_BOTTOM | GRAVITY_LEFT, mBaseTextPaint));
             if (glyphData.hasValidShiftText()) {
                 mGlyphDrawables.add(new GlyphDrawable(glyphData.getShiftText(), new RectF(),
-                        GRAVITY_TOP | (isCenter ? GRAVITY_CENTER_HORIZONTAL : GRAVITY_LEFT),
-                        mModifierTextPaint));
+                        GRAVITY_TOP | GRAVITY_LEFT, mModifierTextPaint));
             }
             if (glyphData.hasValidAltGrText()) {
                 mGlyphDrawables.add(new GlyphDrawable(glyphData.getAltGrText(), new RectF(),
                         GRAVITY_BOTTOM | GRAVITY_RIGHT, mModifierTextPaint));
             }
-            if (glyphData.hasValidAltShiftText()) {
+            if (glyphData.hasValidAltGrShiftText()) {
                 mGlyphDrawables.add(new GlyphDrawable(glyphData.getAltGrShiftText(), new RectF(),
                         GRAVITY_TOP | GRAVITY_RIGHT, mModifierTextPaint));
             }
@@ -246,15 +250,19 @@
                 float centerY = keyHeight / 2;
                 if ((glyph.gravity & GRAVITY_LEFT) != 0) {
                     centerX -= keyWidth / 4;
+                    centerX += mTextPadding / 2;
                 }
                 if ((glyph.gravity & GRAVITY_RIGHT) != 0) {
                     centerX += keyWidth / 4;
+                    centerX -= mTextPadding / 2;
                 }
                 if ((glyph.gravity & GRAVITY_TOP) != 0) {
                     centerY -= keyHeight / 4;
+                    centerY += mTextPadding / 2;
                 }
                 if ((glyph.gravity & GRAVITY_BOTTOM) != 0) {
                     centerY += keyHeight / 4;
+                    centerY -= mTextPadding / 2;
                 }
                 Rect textBounds = new Rect();
                 glyph.paint.getTextBounds(glyph.text, 0, glyph.text.length(), textBounds);
@@ -285,9 +293,9 @@
     private static class UnsureTypingKey extends TypingKey {
 
         private UnsureTypingKey(@Nullable PhysicalKeyLayout.KeyGlyph glyphData,
-                RectF keyRect, float keyRadius, Paint keyPaint, Paint baseTextPaint,
-                Paint modifierTextPaint) {
-            super(glyphData, keyRect, keyRadius, createGreyedOutPaint(keyPaint),
+                RectF keyRect, float keyRadius, float textPadding, Paint keyPaint,
+                Paint baseTextPaint, Paint modifierTextPaint) {
+            super(glyphData, keyRect, keyRadius, textPadding, createGreyedOutPaint(keyPaint),
                     createGreyedOutPaint(baseTextPaint), createGreyedOutPaint(modifierTextPaint));
         }
     }
@@ -402,8 +410,11 @@
         private final Paint mSecondaryGlyphPaint;
         private final int mKeyPadding;
         private final int mKeyboardPadding;
+        private final float mTextPadding;
         private final float mKeyRadius;
         private final float mBackgroundRadius;
+        private final float mSpToPxMultiplier;
+        private final Paint.FontMetrics mFontMetrics;
 
         private ResourceProvider(Context context) {
             mKeyPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
@@ -414,8 +425,10 @@
                     KEY_RADIUS_IN_DP, context.getResources().getDisplayMetrics());
             mBackgroundRadius = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                     KEYBOARD_RADIUS_IN_DP, context.getResources().getDisplayMetrics());
-            int textSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
-                    GLYPH_TEXT_SIZE_IN_SP, context.getResources().getDisplayMetrics());
+            mSpToPxMultiplier = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 1,
+                    context.getResources().getDisplayMetrics());
+            mTextPadding = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+                    TEXT_PADDING_IN_DP, context.getResources().getDisplayMetrics());
             boolean isDark = (context.getResources().getConfiguration().uiMode
                     & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
             int typingKeyColor = context.getColor(
@@ -430,15 +443,37 @@
             int backgroundColor = context.getColor(
                     isDark ? android.R.color.system_surface_container_dark
                             : android.R.color.system_surface_container_light);
-            mPrimaryGlyphPaint = createTextPaint(primaryGlyphColor, textSize,
+            mPrimaryGlyphPaint = createTextPaint(primaryGlyphColor,
+                    MIN_GLYPH_TEXT_SIZE_IN_SP * mSpToPxMultiplier,
                     Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD));
-            mSecondaryGlyphPaint = createTextPaint(secondaryGlyphColor, textSize,
+            mSecondaryGlyphPaint = createTextPaint(secondaryGlyphColor,
+                    MIN_GLYPH_TEXT_SIZE_IN_SP * mSpToPxMultiplier,
                     Typeface.create(Typeface.SANS_SERIF, Typeface.NORMAL));
+            mFontMetrics = mPrimaryGlyphPaint.getFontMetrics();
             mTypingKeyPaint = createFillPaint(typingKeyColor);
             mSpecialKeyPaint = createFillPaint(specialKeyColor);
             mBackgroundPaint = createFillPaint(backgroundColor);
         }
 
+        private void calculateBestTextSizeForKey(float keyHeight) {
+            int textSize = (int) (mSpToPxMultiplier * MIN_GLYPH_TEXT_SIZE_IN_SP) + 1;
+            while (textSize < mSpToPxMultiplier * MAX_GLYPH_TEXT_SIZE_IN_SP) {
+                updateTextSize(textSize);
+                if (mFontMetrics.bottom - mFontMetrics.top + 3 * mTextPadding > keyHeight / 2) {
+                    textSize--;
+                    break;
+                }
+                textSize++;
+            }
+            updateTextSize(textSize);
+        }
+
+        private void updateTextSize(float textSize) {
+            mPrimaryGlyphPaint.setTextSize(textSize);
+            mSecondaryGlyphPaint.setTextSize(textSize);
+            mPrimaryGlyphPaint.getFontMetrics(mFontMetrics);
+        }
+
         private Paint getBackgroundPaint() {
             return mBackgroundPaint;
         }
@@ -467,6 +502,10 @@
             return mKeyboardPadding;
         }
 
+        private float getTextPadding() {
+            return mTextPadding;
+        }
+
         private float getKeyRadius() {
             return mKeyRadius;
         }
@@ -476,7 +515,8 @@
         }
     }
 
-    private static Paint createTextPaint(@ColorInt int textColor, int textSize, Typeface typeface) {
+    private static Paint createTextPaint(@ColorInt int textColor, float textSize,
+            Typeface typeface) {
         Paint paint = new Paint();
         paint.setColor(textColor);
         paint.setStyle(Paint.Style.FILL);
diff --git a/core/java/android/hardware/input/PhysicalKeyLayout.java b/core/java/android/hardware/input/PhysicalKeyLayout.java
index 844e02f..cff444f 100644
--- a/core/java/android/hardware/input/PhysicalKeyLayout.java
+++ b/core/java/android/hardware/input/PhysicalKeyLayout.java
@@ -336,11 +336,13 @@
             return "";
         }
         int utf8Char = (kcm.get(keyCode, modifierState) & KeyCharacterMap.COMBINING_ACCENT_MASK);
+        if (utf8Char == 0) {
+            return "";
+        }
         if (Character.isValidCodePoint(utf8Char)) {
             return String.valueOf(Character.toChars(utf8Char));
-        } else {
-            return String.valueOf(kcm.getDisplayLabel(keyCode));
         }
+        return "□";
     }
 
     private static LayoutKey getKey(int keyCode, float keyWeight) {
@@ -434,10 +436,11 @@
         }
 
         public boolean hasValidAltGrText() {
-            return !TextUtils.isEmpty(mAltGrText) && !TextUtils.equals(mBaseText, mAltGrText);
+            return !TextUtils.isEmpty(mAltGrText) && !TextUtils.equals(mBaseText, mAltGrText)
+                    && !TextUtils.equals(mShiftText, mAltGrText);
         }
 
-        public boolean hasValidAltShiftText() {
+        public boolean hasValidAltGrShiftText() {
             return !TextUtils.isEmpty(mAltGrShiftText)
                     && !TextUtils.equals(mBaseText, mAltGrShiftText)
                     && !TextUtils.equals(mAltGrText, mAltGrShiftText)
diff --git a/core/java/android/hardware/input/VirtualKeyboard.java b/core/java/android/hardware/input/VirtualKeyboard.java
index 6eb2ae3..6a7d195 100644
--- a/core/java/android/hardware/input/VirtualKeyboard.java
+++ b/core/java/android/hardware/input/VirtualKeyboard.java
@@ -18,7 +18,9 @@
 
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.companion.virtual.IVirtualDevice;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -66,4 +68,15 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * @return The id of the {@link android.view.InputDevice} corresponding to this keyboard.
+     * @hide
+     */
+    @SuppressLint("UnflaggedApi") // @TestApi without associated feature.
+    @TestApi
+    @Override
+    public int getInputDeviceId() {
+        return super.getInputDeviceId();
+    }
 }
diff --git a/core/java/android/service/notification/flags.aconfig b/core/java/android/service/notification/flags.aconfig
index 446fe3d..c5acc2c 100644
--- a/core/java/android/service/notification/flags.aconfig
+++ b/core/java/android/service/notification/flags.aconfig
@@ -1,4 +1,5 @@
 package: "android.service.notification"
+container: "system"
 
 flag {
   name: "ranking_update_ashmem"
@@ -12,6 +13,7 @@
   namespace: "systemui"
   description: "This flag controls the redacting of sensitive notifications from untrusted NotificationListenerServices"
   bug: "306271190"
+  is_exported: true
 }
 
 flag {
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index bd9f504..83683ca 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -2364,6 +2364,7 @@
 
         synchronized (mLock) {
             if (!isActiveLocked()) {
+                Log.w(TAG, "onAuthenticationResult(): sessionId=" + mSessionId + " not active");
                 return;
             }
             mState = STATE_ACTIVE;
@@ -2380,6 +2381,7 @@
             }
             if (data == null) {
                 // data is set to null when result is not RESULT_OK
+                Log.i(TAG, "onAuthenticationResult(): empty intent");
                 return;
             }
 
diff --git a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
index dc5e0e5..88ca2a4 100644
--- a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
+++ b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
@@ -66,6 +66,7 @@
 
     @Nullable
     private static volatile IImeTracker sTrackerServiceCache = null;
+    private static int sCurStartInputSeq = 0;
 
     /**
      * @return {@code true} if {@link IInputMethodManager} is available.
@@ -327,6 +328,7 @@
         }
     }
 
+    // TODO(b/293640003): Remove method once Flags.useZeroJankProxy() is enabled.
     @AnyThread
     @NonNull
     @RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)
@@ -353,6 +355,41 @@
         }
     }
 
+    /**
+     * Returns a sequence number for startInput.
+     */
+    @AnyThread
+    @NonNull
+    @RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)
+    static int startInputOrWindowGainedFocusAsync(@StartInputReason int startInputReason,
+            @NonNull IInputMethodClient client, @Nullable IBinder windowToken,
+            @StartInputFlags int startInputFlags,
+            @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode,
+            @WindowManager.LayoutParams.Flags int windowFlags, @Nullable EditorInfo editorInfo,
+            @Nullable IRemoteInputConnection remoteInputConnection,
+            @Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
+            int unverifiedTargetSdkVersion, @UserIdInt int userId,
+            @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
+        final IInputMethodManager service = getService();
+        if (service == null) {
+            return -1;
+        }
+        try {
+            service.startInputOrWindowGainedFocusAsync(startInputReason, client, windowToken,
+                    startInputFlags, softInputMode, windowFlags, editorInfo, remoteInputConnection,
+                    remoteAccessibilityInputConnection, unverifiedTargetSdkVersion, userId,
+                    imeDispatcher, advanceAngGetStartInputSequenceNumber());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+        return sCurStartInputSeq;
+    }
+
+    private static int advanceAngGetStartInputSequenceNumber() {
+        return ++sCurStartInputSeq;
+    }
+
+
     @AnyThread
     static void showInputMethodPickerFromClient(@NonNull IInputMethodClient client,
             int auxiliarySubtypeMode) {
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index f4b09df..72125ba 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -321,6 +321,22 @@
     };
 
     /**
+     * A runnable that reports {@link InputConnection} opened event for calls to
+     * {@link IInputMethodManagerGlobalInvoker#startInputOrWindowGainedFocusAsync}.
+     */
+    private abstract static class ReportInputConnectionOpenedRunner implements Runnable {
+        /**
+         * Sequence number to track startInput requests to
+         * {@link IInputMethodManagerGlobalInvoker#startInputOrWindowGainedFocusAsync}
+         */
+        int mSequenceNum;
+        ReportInputConnectionOpenedRunner(int sequenceNum) {
+            this.mSequenceNum = sequenceNum;
+        }
+    }
+    private ReportInputConnectionOpenedRunner mReportInputConnectionOpenedRunner;
+
+    /**
      * Ensures that {@link #sInstance} becomes non-{@code null} for application that have directly
      * or indirectly relied on {@link #sInstance} via reflection or something like that.
      *
@@ -691,6 +707,7 @@
     private static final int MSG_UNBIND_ACCESSIBILITY_SERVICE = 12;
     private static final int MSG_SET_INTERACTIVE = 13;
     private static final int MSG_ON_SHOW_REQUESTED = 31;
+    private static final int MSG_START_INPUT_RESULT = 40;
 
     /**
      * Calling this will invalidate Local stylus handwriting availability Cache which
@@ -1045,7 +1062,7 @@
                     return;
                 }
                 case MSG_BIND: {
-                    final InputBindResult res = (InputBindResult)msg.obj;
+                    final InputBindResult res = (InputBindResult) msg.obj;
                     if (DEBUG) {
                         Log.i(TAG, "handleMessage: MSG_BIND " + res.sequence + "," + res.id);
                     }
@@ -1071,6 +1088,60 @@
                     startInputInner(StartInputReason.BOUND_TO_IMMS, null, 0, 0, 0);
                     return;
                 }
+
+                case MSG_START_INPUT_RESULT: {
+                    final InputBindResult res = (InputBindResult) msg.obj;
+                    final int startInputSeq = msg.arg1;
+                    if (res == null) {
+                        // IMMS logs .wtf already.
+                        return;
+                    }
+                    if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res);
+                    synchronized (mH) {
+                        if (res.id != null) {
+                            updateInputChannelLocked(res.channel);
+                            mCurMethod = res.method; // for @UnsupportedAppUsage
+                            mCurBindState = new BindState(res);
+                            mAccessibilityInputMethodSession.clear();
+                            if (res.accessibilitySessions != null) {
+                                for (int i = 0; i < res.accessibilitySessions.size(); i++) {
+                                    IAccessibilityInputMethodSessionInvoker wrapper =
+                                            IAccessibilityInputMethodSessionInvoker.createOrNull(
+                                                    res.accessibilitySessions.valueAt(i));
+                                    if (wrapper != null) {
+                                        mAccessibilityInputMethodSession.append(
+                                                res.accessibilitySessions.keyAt(i), wrapper);
+                                    }
+                                }
+                            }
+                            mCurId = res.id; // for @UnsupportedAppUsage
+                        } else if (res.channel != null && res.channel != mCurChannel) {
+                            res.channel.dispose();
+                        }
+                        switch (res.result) {
+                            case InputBindResult.ResultCode.ERROR_NOT_IME_TARGET_WINDOW:
+                                mRestartOnNextWindowFocus = true;
+                                mServedView = null;
+                                break;
+                        }
+                        if (mCompletions != null) {
+                            if (isImeSessionAvailableLocked()) {
+                                mCurBindState.mImeSession.displayCompletions(mCompletions);
+                            }
+                        }
+
+                        if (res != null
+                                && res.method != null
+                                && mServedView != null
+                                && mReportInputConnectionOpenedRunner != null
+                                && mReportInputConnectionOpenedRunner.mSequenceNum
+                                        == startInputSeq) {
+                            mReportInputConnectionOpenedRunner.run();
+                        }
+                        mReportInputConnectionOpenedRunner = null;
+                    }
+                    return;
+                }
                 case MSG_UNBIND: {
                     final int sequence = msg.arg1;
                     @UnbindReason
@@ -1322,6 +1393,12 @@
         }
 
         @Override
+        public void onStartInputResult(InputBindResult res, int startInputSeq) {
+            mH.obtainMessage(MSG_START_INPUT_RESULT, startInputSeq, -1 /* unused */, res)
+                    .sendToTarget();
+        }
+
+        @Override
         public void onBindAccessibilityService(InputBindResult res, int id) {
             mH.obtainMessage(MSG_BIND_ACCESSIBILITY_SERVICE, id, 0, res).sendToTarget();
         }
@@ -2010,6 +2087,7 @@
             mServedConnecting = false;
             clearConnectionLocked();
         }
+        mReportInputConnectionOpenedRunner = null;
         // Clear the back callbacks held by the ime dispatcher to avoid memory leaks.
         mImeDispatcher.clear();
     }
@@ -3080,14 +3158,52 @@
             final int targetUserId = editorInfo.targetInputMethodUser != null
                     ? editorInfo.targetInputMethodUser.getIdentifier() : UserHandle.myUserId();
             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMM.startInputOrWindowGainedFocus");
-            res = IInputMethodManagerGlobalInvoker.startInputOrWindowGainedFocus(
-                    startInputReason, mClient, windowGainingFocus, startInputFlags,
-                    softInputMode, windowFlags, editorInfo, servedInputConnection,
-                    servedInputConnection == null ? null
-                            : servedInputConnection.asIRemoteAccessibilityInputConnection(),
-                    view.getContext().getApplicationInfo().targetSdkVersion, targetUserId,
-                    mImeDispatcher);
+
+            int startInputSeq = -1;
+            if (Flags.useZeroJankProxy()) {
+                // async result delivered via MSG_START_INPUT_RESULT.
+                startInputSeq = IInputMethodManagerGlobalInvoker.startInputOrWindowGainedFocusAsync(
+                        startInputReason, mClient, windowGainingFocus, startInputFlags,
+                        softInputMode, windowFlags, editorInfo, servedInputConnection,
+                        servedInputConnection == null ? null
+                                : servedInputConnection.asIRemoteAccessibilityInputConnection(),
+                        view.getContext().getApplicationInfo().targetSdkVersion, targetUserId,
+                        mImeDispatcher);
+            } else {
+                res = IInputMethodManagerGlobalInvoker.startInputOrWindowGainedFocus(
+                        startInputReason, mClient, windowGainingFocus, startInputFlags,
+                        softInputMode, windowFlags, editorInfo, servedInputConnection,
+                        servedInputConnection == null ? null
+                                : servedInputConnection.asIRemoteAccessibilityInputConnection(),
+                        view.getContext().getApplicationInfo().targetSdkVersion, targetUserId,
+                        mImeDispatcher);
+            }
             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+            if (Flags.useZeroJankProxy()) {
+                // Create a runnable for delayed notification to the app that the InputConnection is
+                // initialized and ready for use.
+                if (ic != null) {
+                    final int seqId = startInputSeq;
+                    mReportInputConnectionOpenedRunner =
+                            new ReportInputConnectionOpenedRunner(startInputSeq) {
+                                @Override
+                                public void run() {
+                                    if (DEBUG) {
+                                        Log.v(TAG, "Calling View.onInputConnectionOpened: view= "
+                                                + view
+                                                + ", ic=" + ic + ", editorInfo=" + editorInfo
+                                                + ", handler="
+                                                + icHandler + ", startInputSeq=" + seqId);
+                                    }
+                                    reportInputConnectionOpened(ic, editorInfo, icHandler, view);
+                                }
+                            };
+                } else {
+                    mReportInputConnectionOpenedRunner = null;
+                }
+                return true;
+            }
+
             if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res);
             if (res == null) {
                 Log.wtf(TAG, "startInputOrWindowGainedFocus must not return"
@@ -3118,6 +3234,7 @@
             } else if (res.channel != null && res.channel != mCurChannel) {
                 res.channel.dispose();
             }
+
             switch (res.result) {
                 case InputBindResult.ResultCode.ERROR_NOT_IME_TARGET_WINDOW:
                     mRestartOnNextWindowFocus = true;
diff --git a/core/java/com/android/internal/inputmethod/IInputMethodClient.aidl b/core/java/com/android/internal/inputmethod/IInputMethodClient.aidl
index 9251d2d..babd9a0 100644
--- a/core/java/com/android/internal/inputmethod/IInputMethodClient.aidl
+++ b/core/java/com/android/internal/inputmethod/IInputMethodClient.aidl
@@ -24,6 +24,7 @@
  */
 oneway interface IInputMethodClient {
     void onBindMethod(in InputBindResult res);
+    void onStartInputResult(in InputBindResult res, int startInputSeq);
     void onBindAccessibilityService(in InputBindResult res, int id);
     void onUnbindMethod(int sequence, int unbindReason);
     void onUnbindAccessibilityService(int sequence, int id);
diff --git a/core/java/com/android/internal/inputmethod/InputBindResult.java b/core/java/com/android/internal/inputmethod/InputBindResult.java
index b6eca07..243b103 100644
--- a/core/java/com/android/internal/inputmethod/InputBindResult.java
+++ b/core/java/com/android/internal/inputmethod/InputBindResult.java
@@ -271,6 +271,7 @@
     public String toString() {
         return "InputBindResult{result=" + getResultString() + " method=" + method + " id=" + id
                 + " sequence=" + sequence
+                + " result=" + result
                 + " isInputMethodSuppressingSpellChecker=" + isInputMethodSuppressingSpellChecker
                 + "}";
     }
diff --git a/core/java/com/android/internal/jank/Cuj.java b/core/java/com/android/internal/jank/Cuj.java
index 48c455a..3662d69 100644
--- a/core/java/com/android/internal/jank/Cuj.java
+++ b/core/java/com/android/internal/jank/Cuj.java
@@ -120,7 +120,7 @@
     public static final int CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY = 84;
     public static final int CUJ_PREDICTIVE_BACK_CROSS_TASK = 85;
     public static final int CUJ_PREDICTIVE_BACK_HOME = 86;
-    public static final int CUJ_LAUNCHER_SEARCH_QSB_OPEN = 87;
+    // 87 is reserved - previously assigned to deprecated CUJ_LAUNCHER_SEARCH_QSB_OPEN.
     public static final int CUJ_BACK_PANEL_ARROW = 88;
     public static final int CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK = 89;
     public static final int CUJ_LAUNCHER_SEARCH_QSB_WEB_SEARCH = 90;
@@ -209,7 +209,6 @@
             CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY,
             CUJ_PREDICTIVE_BACK_CROSS_TASK,
             CUJ_PREDICTIVE_BACK_HOME,
-            CUJ_LAUNCHER_SEARCH_QSB_OPEN,
             CUJ_BACK_PANEL_ARROW,
             CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK,
             CUJ_LAUNCHER_SEARCH_QSB_WEB_SEARCH,
@@ -304,7 +303,6 @@
         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_CROSS_ACTIVITY;
         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PREDICTIVE_BACK_CROSS_TASK] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_CROSS_TASK;
         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PREDICTIVE_BACK_HOME] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_HOME;
-        CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_SEARCH_QSB_OPEN] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_SEARCH_QSB_OPEN;
         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_BACK_PANEL_ARROW] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__BACK_PANEL_ARROW;
         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_CLOSE_ALL_APPS_BACK;
         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_SEARCH_QSB_WEB_SEARCH] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_SEARCH_QSB_WEB_SEARCH;
@@ -480,8 +478,6 @@
                 return "PREDICTIVE_BACK_CROSS_TASK";
             case CUJ_PREDICTIVE_BACK_HOME:
                 return "PREDICTIVE_BACK_HOME";
-            case CUJ_LAUNCHER_SEARCH_QSB_OPEN:
-                return "LAUNCHER_SEARCH_QSB_OPEN";
             case CUJ_BACK_PANEL_ARROW:
                 return "BACK_PANEL_ARROW";
             case CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK:
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index e95127b..b90f8bf 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -69,6 +69,8 @@
     boolean hideSoftInput(in IInputMethodClient client, @nullable IBinder windowToken,
             in @nullable ImeTracker.Token statsToken, int flags,
             in @nullable ResultReceiver resultReceiver, int reason);
+
+    // TODO(b/293640003): Remove method once Flags.useZeroJankProxy() is enabled.
     // If windowToken is null, this just does startInput().  Otherwise this reports that a window
     // has gained focus, and if 'editorInfo' is non-null then also does startInput.
     // @NonNull
@@ -85,6 +87,21 @@
             int unverifiedTargetSdkVersion, int userId,
             in ImeOnBackInvokedDispatcher imeDispatcher);
 
+    // If windowToken is null, this just does startInput().  Otherwise this reports that a window
+    // has gained focus, and if 'editorInfo' is non-null then also does startInput.
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+            + "android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)")
+    void startInputOrWindowGainedFocusAsync(
+            /* @StartInputReason */ int startInputReason,
+            in IInputMethodClient client, in @nullable IBinder windowToken,
+            /* @StartInputFlags */ int startInputFlags,
+            /* @android.view.WindowManager.LayoutParams.SoftInputModeFlags */ int softInputMode,
+            /* @android.view.WindowManager.LayoutParams.Flags */ int windowFlags,
+            in @nullable EditorInfo editorInfo, in @nullable IRemoteInputConnection inputConnection,
+            in @nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
+            int unverifiedTargetSdkVersion, int userId,
+            in ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq);
+
     void showInputMethodPickerFromClient(in IInputMethodClient client,
             int auxiliarySubtypeMode);
 
@@ -156,6 +173,7 @@
                 in String delegatePackageName,
                 in String delegatorPackageName);
 
+    // TODO(b/293640003): introduce a new API method to provide async way to return boolean.
     /** Accepts and starts a stylus handwriting session for the delegate view **/
     boolean acceptStylusHandwritingDelegation(in IInputMethodClient client, in int userId,
             in String delegatePackageName, in String delegatorPackageName, int flags);
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 48ef7e6..ebf4cca 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -43,7 +43,6 @@
 import android.app.PictureInPictureUiState;
 import android.app.ResourcesManager;
 import android.app.servertransaction.ActivityConfigurationChangeItem;
-import android.app.servertransaction.ActivityLifecycleItem;
 import android.app.servertransaction.ActivityRelaunchItem;
 import android.app.servertransaction.ClientTransaction;
 import android.app.servertransaction.ClientTransactionItem;
@@ -75,7 +74,6 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.content.ReferrerIntent;
-import com.android.window.flags.Flags;
 
 import org.junit.After;
 import org.junit.Before;
@@ -230,7 +228,7 @@
         try {
             // Send process level config change.
             ClientTransaction transaction = newTransaction(activityThread);
-            addClientTransactionItem(transaction, ConfigurationChangeItem.obtain(
+            transaction.addTransactionItem(ConfigurationChangeItem.obtain(
                     newConfig, DEVICE_ID_INVALID));
             appThread.scheduleTransaction(transaction);
             InstrumentationRegistry.getInstrumentation().waitForIdleSync();
@@ -247,7 +245,7 @@
             newConfig.seq++;
             newConfig.smallestScreenWidthDp++;
             transaction = newTransaction(activityThread);
-            addClientTransactionItem(transaction, ActivityConfigurationChangeItem.obtain(
+            transaction.addTransactionItem(ActivityConfigurationChangeItem.obtain(
                     activity.getActivityToken(), newConfig));
             appThread.scheduleTransaction(transaction);
             InstrumentationRegistry.getInstrumentation().waitForIdleSync();
@@ -448,16 +446,16 @@
         activity.mTestLatch = new CountDownLatch(1);
 
         ClientTransaction transaction = newTransaction(activityThread);
-        addClientTransactionItem(transaction, ConfigurationChangeItem.obtain(
+        transaction.addTransactionItem(ConfigurationChangeItem.obtain(
                 processConfigLandscape, DEVICE_ID_INVALID));
         appThread.scheduleTransaction(transaction);
 
         transaction = newTransaction(activityThread);
-        addClientTransactionItem(transaction, ActivityConfigurationChangeItem.obtain(
+        transaction.addTransactionItem(ActivityConfigurationChangeItem.obtain(
                 activity.getActivityToken(), activityConfigLandscape));
-        addClientTransactionItem(transaction, ConfigurationChangeItem.obtain(
+        transaction.addTransactionItem(ConfigurationChangeItem.obtain(
                 processConfigPortrait, DEVICE_ID_INVALID));
-        addClientTransactionItem(transaction, ActivityConfigurationChangeItem.obtain(
+        transaction.addTransactionItem(ActivityConfigurationChangeItem.obtain(
                 activity.getActivityToken(), activityConfigPortrait));
         appThread.scheduleTransaction(transaction);
 
@@ -847,8 +845,8 @@
                         false /* shouldSendCompatFakeFocus*/);
 
         final ClientTransaction transaction = newTransaction(activity);
-        addClientTransactionItem(transaction, callbackItem);
-        addClientTransactionItem(transaction, resumeStateRequest);
+        transaction.addTransactionItem(callbackItem);
+        transaction.addTransactionItem(resumeStateRequest);
 
         return transaction;
     }
@@ -860,7 +858,7 @@
                         false /* shouldSendCompatFakeFocus */);
 
         final ClientTransaction transaction = newTransaction(activity);
-        addClientTransactionItem(transaction, resumeStateRequest);
+        transaction.addTransactionItem(resumeStateRequest);
 
         return transaction;
     }
@@ -871,7 +869,7 @@
                 activity.getActivityToken(), 0 /* configChanges */);
 
         final ClientTransaction transaction = newTransaction(activity);
-        addClientTransactionItem(transaction, stopStateRequest);
+        transaction.addTransactionItem(stopStateRequest);
 
         return transaction;
     }
@@ -883,7 +881,7 @@
                 activity.getActivityToken(), config);
 
         final ClientTransaction transaction = newTransaction(activity);
-        addClientTransactionItem(transaction, item);
+        transaction.addTransactionItem(item);
 
         return transaction;
     }
@@ -895,7 +893,7 @@
                 resume);
 
         final ClientTransaction transaction = newTransaction(activity);
-        addClientTransactionItem(transaction, item);
+        transaction.addTransactionItem(item);
 
         return transaction;
     }
@@ -910,17 +908,6 @@
         return ClientTransaction.obtain(activityThread.getApplicationThread());
     }
 
-    private static void addClientTransactionItem(@NonNull ClientTransaction transaction,
-            @NonNull ClientTransactionItem item) {
-        if (Flags.bundleClientTransactionFlag()) {
-            transaction.addTransactionItem(item);
-        } else if (item.isActivityLifecycleItem()) {
-            transaction.setLifecycleStateRequest((ActivityLifecycleItem) item);
-        } else {
-            transaction.addCallback(item);
-        }
-    }
-
     // Test activity
     public static class TestActivity extends Activity {
         static final String PIP_REQUESTED_OVERRIDE_ENTER = "pip_requested_override_enter";
diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
index 95d5049..213fd7b 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
@@ -16,10 +16,13 @@
 
 package android.app.servertransaction;
 
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
+
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
+import static com.android.window.flags.Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG;
+
 import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 
 import android.hardware.display.DisplayManager;
@@ -28,12 +31,14 @@
 import android.os.Handler;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.view.DisplayInfo;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -49,6 +54,10 @@
 @SmallTest
 @Presubmit
 public class ClientTransactionListenerControllerTest {
+
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
+
     @Mock
     private IDisplayManager mIDisplayManager;
     @Mock
@@ -60,12 +69,12 @@
 
     @Before
     public void setup() {
+        mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
+
         MockitoAnnotations.initMocks(this);
         mDisplayManager = new DisplayManagerGlobal(mIDisplayManager);
         mHandler = getInstrumentation().getContext().getMainThreadHandler();
-        mController = spy(ClientTransactionListenerController.createInstanceForTesting(
-                mDisplayManager));
-        doReturn(true).when(mController).isBundleClientTransactionFlagEnabled();
+        mController = ClientTransactionListenerController.createInstanceForTesting(mDisplayManager);
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java
index d10cf16..5272416 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java
@@ -16,16 +16,22 @@
 
 package android.app.servertransaction;
 
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
+
+import static com.android.window.flags.Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG;
+
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
 import android.app.ClientTransactionHandler;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -43,31 +49,28 @@
 @Presubmit
 public class ClientTransactionTests {
 
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
+
     @Test
     public void testPreExecute() {
-        final ClientTransactionItem callback1 = mock(ClientTransactionItem.class);
-        final ClientTransactionItem callback2 = mock(ClientTransactionItem.class);
-        final ActivityLifecycleItem stateRequest = mock(ActivityLifecycleItem.class);
-        final ClientTransactionHandler clientTransactionHandler =
-                mock(ClientTransactionHandler.class);
+        mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
 
-        final ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
-        transaction.addCallback(callback1);
-        transaction.addCallback(callback2);
-        transaction.setLifecycleStateRequest(stateRequest);
-
-        transaction.preExecute(clientTransactionHandler);
-
-        verify(callback1, times(1)).preExecute(clientTransactionHandler);
-        verify(callback2, times(1)).preExecute(clientTransactionHandler);
-        verify(stateRequest, times(1)).preExecute(clientTransactionHandler);
+        testPreExecuteInner();
     }
 
     @Test
-    public void testPreExecuteTransactionItems() {
+    public void testPreExecute_bundleClientTransaction() {
+        mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
+
+        testPreExecuteInner();
+    }
+
+    private void testPreExecuteInner() {
         final ClientTransactionItem callback1 = mock(ClientTransactionItem.class);
         final ClientTransactionItem callback2 = mock(ClientTransactionItem.class);
         final ActivityLifecycleItem stateRequest = mock(ActivityLifecycleItem.class);
+        doReturn(true).when(stateRequest).isActivityLifecycleItem();
         final ClientTransactionHandler clientTransactionHandler =
                 mock(ClientTransactionHandler.class);
 
@@ -78,8 +81,8 @@
 
         transaction.preExecute(clientTransactionHandler);
 
-        verify(callback1, times(1)).preExecute(clientTransactionHandler);
-        verify(callback2, times(1)).preExecute(clientTransactionHandler);
-        verify(stateRequest, times(1)).preExecute(clientTransactionHandler);
+        verify(callback1).preExecute(clientTransactionHandler);
+        verify(callback2).preExecute(clientTransactionHandler);
+        verify(stateRequest).preExecute(clientTransactionHandler);
     }
 }
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
index 2315a58..adb6f2a 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
@@ -25,6 +25,9 @@
 import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
 import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE;
 import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED;
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
+
+import static com.android.window.flags.Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG;
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
@@ -51,12 +54,14 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.util.ArrayMap;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.InOrder;
@@ -83,6 +88,9 @@
 @Presubmit
 public class TransactionExecutorTests {
 
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
+
     @Mock
     private ClientTransactionHandler mTransactionHandler;
     @Mock
@@ -240,29 +248,19 @@
 
     @Test
     public void testTransactionResolution() {
-        ClientTransactionItem callback1 = mock(ClientTransactionItem.class);
-        when(callback1.getPostExecutionState()).thenReturn(UNDEFINED);
-        ClientTransactionItem callback2 = mock(ClientTransactionItem.class);
-        when(callback2.getPostExecutionState()).thenReturn(UNDEFINED);
+        mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
 
-        ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
-        transaction.addCallback(callback1);
-        transaction.addCallback(callback2);
-        transaction.setLifecycleStateRequest(mActivityLifecycleItem);
-
-        transaction.preExecute(mTransactionHandler);
-        mExecutor.execute(transaction);
-
-        InOrder inOrder = inOrder(mTransactionHandler, callback1, callback2,
-                mActivityLifecycleItem);
-        inOrder.verify(callback1).execute(eq(mTransactionHandler), any());
-        inOrder.verify(callback2).execute(eq(mTransactionHandler), any());
-        inOrder.verify(mActivityLifecycleItem).execute(eq(mTransactionHandler), eq(mClientRecord),
-                any());
+        testTransactionResolutionInner();
     }
 
     @Test
-    public void testExecuteTransactionItems_transactionResolution() {
+    public void testTransactionResolution_bundleClientTransaction() {
+        mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
+
+        testTransactionResolutionInner();
+    }
+
+    private void testTransactionResolutionInner() {
         ClientTransactionItem callback1 = mock(ClientTransactionItem.class);
         when(callback1.getPostExecutionState()).thenReturn(UNDEFINED);
         ClientTransactionItem callback2 = mock(ClientTransactionItem.class);
@@ -286,38 +284,19 @@
 
     @Test
     public void testDoNotLaunchDestroyedActivity() {
-        final Map<IBinder, DestroyActivityItem> activitiesToBeDestroyed = new ArrayMap<>();
-        when(mTransactionHandler.getActivitiesToBeDestroyed()).thenReturn(activitiesToBeDestroyed);
-        // Assume launch transaction is still in queue, so there is no client record.
-        when(mTransactionHandler.getActivityClient(any())).thenReturn(null);
+        mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
 
-        // An incoming destroy transaction enters binder thread (preExecute).
-        final IBinder token = mock(IBinder.class);
-        final ClientTransaction destroyTransaction = ClientTransaction.obtain(null /* client */);
-        destroyTransaction.setLifecycleStateRequest(
-                DestroyActivityItem.obtain(token, false /* finished */, 0 /* configChanges */));
-        destroyTransaction.preExecute(mTransactionHandler);
-        // The activity should be added to to-be-destroyed container.
-        assertEquals(1, activitiesToBeDestroyed.size());
-
-        // A previous queued launch transaction runs on main thread (execute).
-        final ClientTransaction launchTransaction = ClientTransaction.obtain(null /* client */);
-        final LaunchActivityItem launchItem =
-                spy(new LaunchActivityItemBuilder(token, new Intent(), new ActivityInfo()).build());
-        launchTransaction.addCallback(launchItem);
-        mExecutor.execute(launchTransaction);
-
-        // The launch transaction should not be executed because its token is in the
-        // to-be-destroyed container.
-        verify(launchItem, never()).execute(any(), any());
-
-        // After the destroy transaction has been executed, the token should be removed.
-        mExecutor.execute(destroyTransaction);
-        assertTrue(activitiesToBeDestroyed.isEmpty());
+        testDoNotLaunchDestroyedActivityInner();
     }
 
     @Test
-    public void testExecuteTransactionItems_doNotLaunchDestroyedActivity() {
+    public void testDoNotLaunchDestroyedActivity_bundleClientTransaction() {
+        mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
+
+        testDoNotLaunchDestroyedActivityInner();
+    }
+
+    private void testDoNotLaunchDestroyedActivityInner() {
         final Map<IBinder, DestroyActivityItem> activitiesToBeDestroyed = new ArrayMap<>();
         when(mTransactionHandler.getActivitiesToBeDestroyed()).thenReturn(activitiesToBeDestroyed);
         // Assume launch transaction is still in queue, so there is no client record.
@@ -350,26 +329,19 @@
 
     @Test
     public void testActivityResultRequiredStateResolution() {
-        when(mTransactionHandler.getActivity(any())).thenReturn(mock(Activity.class));
+        mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
 
-        PostExecItem postExecItem = new PostExecItem(ON_RESUME);
-
-        ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
-        transaction.addCallback(postExecItem);
-
-        // Verify resolution that should get to onPause
-        mClientRecord.setState(ON_RESUME);
-        mExecutor.executeCallbacks(transaction);
-        verify(mExecutor).cycleToPath(eq(mClientRecord), eq(ON_PAUSE), eq(transaction));
-
-        // Verify resolution that should get to onStart
-        mClientRecord.setState(ON_STOP);
-        mExecutor.executeCallbacks(transaction);
-        verify(mExecutor).cycleToPath(eq(mClientRecord), eq(ON_START), eq(transaction));
+        testActivityResultRequiredStateResolutionInner();
     }
 
     @Test
-    public void testExecuteTransactionItems_activityResultRequiredStateResolution() {
+    public void testActivityResultRequiredStateResolution_bundleClientTransaction() {
+        mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
+
+        testActivityResultRequiredStateResolutionInner();
+    }
+
+    private void testActivityResultRequiredStateResolutionInner() {
         when(mTransactionHandler.getActivity(any())).thenReturn(mock(Activity.class));
 
         PostExecItem postExecItem = new PostExecItem(ON_RESUME);
@@ -379,12 +351,12 @@
 
         // Verify resolution that should get to onPause
         mClientRecord.setState(ON_RESUME);
-        mExecutor.executeTransactionItems(transaction);
+        mExecutor.execute(transaction);
         verify(mExecutor).cycleToPath(eq(mClientRecord), eq(ON_PAUSE), eq(transaction));
 
         // Verify resolution that should get to onStart
         mClientRecord.setState(ON_STOP);
-        mExecutor.executeTransactionItems(transaction);
+        mExecutor.execute(transaction);
         verify(mExecutor).cycleToPath(eq(mClientRecord), eq(ON_START), eq(transaction));
     }
 
@@ -523,18 +495,19 @@
 
     @Test(expected = IllegalArgumentException.class)
     public void testActivityItemNullRecordThrowsException() {
-        final ActivityTransactionItem activityItem = mock(ActivityTransactionItem.class);
-        when(activityItem.getPostExecutionState()).thenReturn(UNDEFINED);
-        final IBinder token = mock(IBinder.class);
-        final ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
-        transaction.addCallback(activityItem);
-        when(mTransactionHandler.getActivityClient(token)).thenReturn(null);
+        mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
 
-        mExecutor.executeCallbacks(transaction);
+        testActivityItemNullRecordThrowsExceptionInner();
     }
 
     @Test(expected = IllegalArgumentException.class)
-    public void testExecuteTransactionItems_activityItemNullRecordThrowsException() {
+    public void testActivityItemNullRecordThrowsException_bundleClientTransaction() {
+        mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
+
+        testActivityItemNullRecordThrowsExceptionInner();
+    }
+
+    private void testActivityItemNullRecordThrowsExceptionInner() {
         final ActivityTransactionItem activityItem = mock(ActivityTransactionItem.class);
         when(activityItem.getPostExecutionState()).thenReturn(UNDEFINED);
         final IBinder token = mock(IBinder.class);
@@ -542,28 +515,24 @@
         transaction.addTransactionItem(activityItem);
         when(mTransactionHandler.getActivityClient(token)).thenReturn(null);
 
-        mExecutor.executeTransactionItems(transaction);
+        mExecutor.execute(transaction);
     }
 
     @Test
     public void testActivityItemExecute() {
-        final ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
-        final ActivityTransactionItem activityItem = mock(ActivityTransactionItem.class);
-        when(activityItem.getPostExecutionState()).thenReturn(UNDEFINED);
-        when(activityItem.getActivityToken()).thenReturn(mActivityToken);
-        transaction.addCallback(activityItem);
-        transaction.setLifecycleStateRequest(mActivityLifecycleItem);
+        mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
 
-        mExecutor.execute(transaction);
-
-        final InOrder inOrder = inOrder(activityItem, mActivityLifecycleItem);
-        inOrder.verify(activityItem).execute(eq(mTransactionHandler), eq(mClientRecord), any());
-        inOrder.verify(mActivityLifecycleItem).execute(eq(mTransactionHandler), eq(mClientRecord),
-                any());
+        testActivityItemExecuteInner();
     }
 
     @Test
-    public void testExecuteTransactionItems_activityItemExecute() {
+    public void testActivityItemExecute_bundleClientTransaction() {
+        mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
+
+        testActivityItemExecuteInner();
+    }
+
+    private void testActivityItemExecuteInner() {
         final ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
         final ActivityTransactionItem activityItem = mock(ActivityTransactionItem.class);
         when(activityItem.getPostExecutionState()).thenReturn(UNDEFINED);
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index c30d216..aa80013 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -20,6 +20,9 @@
 import static android.app.servertransaction.TestUtils.mergedConfig;
 import static android.app.servertransaction.TestUtils.referrerIntentList;
 import static android.app.servertransaction.TestUtils.resultInfoList;
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
+
+import static com.android.window.flags.Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG;
 
 import static org.junit.Assert.assertEquals;
 
@@ -36,11 +39,13 @@
 import android.os.Parcelable;
 import android.os.PersistableBundle;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -60,6 +65,9 @@
 @Presubmit
 public class TransactionParcelTests {
 
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
+
     private Parcel mParcel;
     private IBinder mActivityToken;
 
@@ -275,6 +283,8 @@
 
     @Test
     public void testClientTransaction() {
+        mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
+
         // Write to parcel
         NewIntentItem callback1 = NewIntentItem.obtain(mActivityToken, new ArrayList<>(), true);
         ActivityConfigurationChangeItem callback2 = ActivityConfigurationChangeItem.obtain(
@@ -300,14 +310,16 @@
 
     @Test
     public void testClientTransactionCallbacksOnly() {
+        mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
+
         // Write to parcel
         NewIntentItem callback1 = NewIntentItem.obtain(mActivityToken, new ArrayList<>(), true);
         ActivityConfigurationChangeItem callback2 = ActivityConfigurationChangeItem.obtain(
                 mActivityToken, config());
 
         ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
-        transaction.addCallback(callback1);
-        transaction.addCallback(callback2);
+        transaction.addTransactionItem(callback1);
+        transaction.addTransactionItem(callback2);
 
         writeAndPrepareForReading(transaction);
 
@@ -321,12 +333,14 @@
 
     @Test
     public void testClientTransactionLifecycleOnly() {
+        mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
+
         // Write to parcel
         StopActivityItem lifecycleRequest = StopActivityItem.obtain(mActivityToken,
                 78 /* configChanges */);
 
         ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
-        transaction.setLifecycleStateRequest(lifecycleRequest);
+        transaction.addTransactionItem(lifecycleRequest);
 
         writeAndPrepareForReading(transaction);
 
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index 310300d..d66c925 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -206,6 +206,8 @@
     srcs: [
         "multivalentTests/src/**/*.kt",
     ],
+    // TODO(b/323188766): Include BubbleStackViewTest once the robolectric issue is fixed.
+    exclude_srcs: ["multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt"],
     static_libs: [
         "junit",
         "androidx.test.runner",
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
new file mode 100644
index 0000000..8989fc5
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2024 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.wm.shell.bubbles
+
+import android.content.Context
+import android.content.Intent
+import android.graphics.Color
+import android.graphics.drawable.Icon
+import android.os.UserHandle
+import android.view.IWindowManager
+import android.view.WindowManager
+import android.view.WindowManagerGlobal
+import androidx.test.annotation.UiThreadTest
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.internal.protolog.common.ProtoLog
+import com.android.launcher3.icons.BubbleIconFactory
+import com.android.wm.shell.R
+import com.android.wm.shell.bubbles.Bubbles.SysuiProxy
+import com.android.wm.shell.common.FloatingContentCoordinator
+import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.taskview.TaskView
+import com.android.wm.shell.taskview.TaskViewTaskController
+import com.google.common.truth.Truth.assertThat
+import com.google.common.util.concurrent.MoreExecutors.directExecutor
+import java.util.concurrent.Semaphore
+import java.util.concurrent.TimeUnit
+import java.util.function.Consumer
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+
+/** Unit tests for [BubbleStackView]. */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class BubbleStackViewTest {
+
+    private val context = ApplicationProvider.getApplicationContext<Context>()
+    private lateinit var positioner: BubblePositioner
+    private lateinit var iconFactory: BubbleIconFactory
+    private lateinit var expandedViewManager: FakeBubbleExpandedViewManager
+    private lateinit var bubbleStackView: BubbleStackView
+    private lateinit var shellExecutor: ShellExecutor
+    private lateinit var windowManager: IWindowManager
+    private lateinit var bubbleTaskViewFactory: BubbleTaskViewFactory
+    private lateinit var bubbleData: BubbleData
+
+    @Before
+    fun setUp() {
+        // Disable protolog tool when running the tests from studio
+        ProtoLog.REQUIRE_PROTOLOGTOOL = false
+        windowManager = WindowManagerGlobal.getWindowManagerService()!!
+        shellExecutor = TestShellExecutor()
+        val windowManager = context.getSystemService(WindowManager::class.java)
+        iconFactory =
+            BubbleIconFactory(
+                context,
+                context.resources.getDimensionPixelSize(R.dimen.bubble_size),
+                context.resources.getDimensionPixelSize(R.dimen.bubble_badge_size),
+                Color.BLACK,
+                context.resources.getDimensionPixelSize(
+                    com.android.internal.R.dimen.importance_ring_stroke_width
+                )
+            )
+        positioner = BubblePositioner(context, windowManager)
+        val bubbleStackViewManager = FakeBubbleStackViewManager()
+        bubbleData =
+            BubbleData(
+                context,
+                BubbleLogger(UiEventLoggerFake()),
+                positioner,
+                BubbleEducationController(context),
+                shellExecutor
+            )
+
+        val sysuiProxy = mock<SysuiProxy>()
+        expandedViewManager = FakeBubbleExpandedViewManager()
+        bubbleTaskViewFactory = FakeBubbleTaskViewFactory()
+        bubbleStackView =
+            BubbleStackView(
+                context,
+                bubbleStackViewManager,
+                positioner,
+                bubbleData,
+                null,
+                FloatingContentCoordinator(),
+                { sysuiProxy },
+                shellExecutor
+            )
+    }
+
+    @UiThreadTest
+    @Test
+    fun addBubble() {
+        val bubble = createAndInflateBubble()
+        bubbleStackView.addBubble(bubble)
+        assertThat(bubbleStackView.bubbleCount).isEqualTo(1)
+    }
+
+    @UiThreadTest
+    @Test
+    fun tapBubbleToExpand() {
+        val bubble = createAndInflateBubble()
+        bubbleStackView.addBubble(bubble)
+        assertThat(bubbleStackView.bubbleCount).isEqualTo(1)
+
+        bubble.iconView!!.performClick()
+        // we're checking the expanded state in BubbleData because that's the source of truth. This
+        // will eventually propagate an update back to the stack view, but setting the entire
+        // pipeline is outside the scope of a unit test.
+        assertThat(bubbleData.isExpanded).isTrue()
+    }
+
+    private fun createAndInflateBubble(): Bubble {
+        val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
+        val icon = Icon.createWithResource(context.resources, R.drawable.bubble_ic_overflow_button)
+        val bubble = Bubble.createAppBubble(intent, UserHandle(1), icon, directExecutor())
+        bubble.setInflateSynchronously(true)
+        bubbleData.notificationEntryUpdated(bubble, true, false)
+
+        val semaphore = Semaphore(0)
+        val callback: BubbleViewInfoTask.Callback =
+            BubbleViewInfoTask.Callback { semaphore.release() }
+        bubble.inflate(
+            callback,
+            context,
+            expandedViewManager,
+            bubbleTaskViewFactory,
+            positioner,
+            bubbleStackView,
+            null,
+            iconFactory,
+            false
+        )
+
+        assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
+        assertThat(bubble.isInflated).isTrue()
+        return bubble
+    }
+
+    private class FakeBubbleStackViewManager : BubbleStackViewManager {
+
+        override fun onAllBubblesAnimatedOut() {}
+
+        override fun updateWindowFlagsForBackpress(interceptBack: Boolean) {}
+
+        override fun checkNotificationPanelExpandedState(callback: Consumer<Boolean>) {}
+
+        override fun hideCurrentInputMethod() {}
+    }
+
+    private class TestShellExecutor : ShellExecutor {
+
+        override fun execute(runnable: Runnable) {
+            runnable.run()
+        }
+
+        override fun executeDelayed(r: Runnable, delayMillis: Long) {
+            r.run()
+        }
+
+        override fun removeCallbacks(r: Runnable) {}
+
+        override fun hasCallback(r: Runnable): Boolean = false
+    }
+
+    private inner class FakeBubbleTaskViewFactory : BubbleTaskViewFactory {
+        override fun create(): BubbleTaskView {
+            val taskViewTaskController = mock<TaskViewTaskController>()
+            val taskView = TaskView(context, taskViewTaskController)
+            return BubbleTaskView(taskView, shellExecutor)
+        }
+    }
+
+    private inner class FakeBubbleExpandedViewManager : BubbleExpandedViewManager {
+
+        override val overflowBubbles: List<Bubble>
+            get() = emptyList()
+
+        override fun setOverflowListener(listener: BubbleData.Listener) {}
+
+        override fun collapseStack() {}
+
+        override fun updateWindowFlagsForBackpress(intercept: Boolean) {}
+
+        override fun promoteBubbleFromOverflow(bubble: Bubble) {}
+
+        override fun removeBubble(key: String, reason: Int) {}
+
+        override fun dismissBubble(bubble: Bubble, reason: Int) {}
+
+        override fun setAppBubbleTaskId(key: String, taskId: Int) {}
+
+        override fun isStackExpanded(): Boolean = false
+
+        override fun isShowingAsBubbleBar(): Boolean = false
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/AndroidManifest.xml b/libs/WindowManager/Shell/tests/unittest/AndroidManifest.xml
index 47a116b..2ef425c 100644
--- a/libs/WindowManager/Shell/tests/unittest/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/tests/unittest/AndroidManifest.xml
@@ -21,6 +21,7 @@
 
     <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
     <uses-permission android:name="android.permission.VIBRATE"/>
+    <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS"/>
 
     <application android:debuggable="true" android:largeHeap="true">
         <uses-library android:name="android.test.mock" />
diff --git a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
index f264b16..32dba5f 100644
--- a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -105,7 +105,6 @@
      */
     private final HashMap<String, AidGroup> mDynamicAidGroups;
 
-    private final ArrayList<String> mPollingLoopFilters;
 
     private final Map<String, Boolean> mAutoTransact;
 
@@ -181,7 +180,6 @@
         this.mDescription = description;
         this.mStaticAidGroups = new HashMap<String, AidGroup>();
         this.mDynamicAidGroups = new HashMap<String, AidGroup>();
-        this.mPollingLoopFilters = new ArrayList<String>();
         this.mAutoTransact = new HashMap<String, Boolean>();
         this.mOffHostName = offHost;
         this.mStaticOffHostName = staticOffHost;
@@ -302,7 +300,6 @@
 
             mStaticAidGroups = new HashMap<String, AidGroup>();
             mDynamicAidGroups = new HashMap<String, AidGroup>();
-            mPollingLoopFilters = new ArrayList<String>();
             mAutoTransact = new HashMap<String, Boolean>();
             mOnHost = onHost;
 
@@ -393,7 +390,6 @@
                     String plf =
                             a.getString(com.android.internal.R.styleable.PollingLoopFilter_name)
                             .toUpperCase(Locale.ROOT);
-                    mPollingLoopFilters.add(plf);
                     boolean autoTransact = a.getBoolean(
                             com.android.internal.R.styleable.PollingLoopFilter_autoTransact,
                             false);
@@ -461,7 +457,7 @@
     @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
     @NonNull
     public List<String> getPollingLoopFilters() {
-        return mPollingLoopFilters;
+        return new ArrayList<>(mAutoTransact.keySet());
     }
 
     /**
@@ -672,12 +668,15 @@
 
     /**
      * Add a Polling Loop Filter. Custom NFC polling frames that match this filter will be
-     * delivered to {@link HostApduService#processPollingFrames(List)}.
-     * @param pollingLoopFilter this polling loop filter to add.
+     * delivered to {@link HostApduService#processPollingFrames(List)}. Adding a key with this or
+     * {@link  ApduServiceInfo#addPollingLoopFilterToAutoTransact(String)} multiple times will
+     * cause the value to be overwritten each time.
+     * @param pollingLoopFilter the polling loop filter to add, must be a  valide hexadecimal string
      */
     @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
     public void addPollingLoopFilter(@NonNull String pollingLoopFilter) {
-        mPollingLoopFilters.add(pollingLoopFilter.toUpperCase(Locale.ROOT));
+        mAutoTransact.put(pollingLoopFilter.toUpperCase(Locale.ROOT), false);
+
     }
 
     /**
@@ -685,13 +684,14 @@
      * device to exit observe mode, just as if
      * {@link android.nfc.NfcAdapter#setTransactionAllowed(boolean)} had been called with true,
      * allowing transactions to proceed. The matching frame will also be delivered to
-     * {@link HostApduService#processPollingFrames(List)}.
+     * {@link HostApduService#processPollingFrames(List)}. Adding a key with this or
+     * {@link  ApduServiceInfo#addPollingLoopFilter(String)} multiple times will
+     * cause the value to be overwritten each time.
      *
-     * @param pollingLoopFilter this polling loop filter to add.
+     * @param pollingLoopFilter the polling loop filter to add, must be a  valide hexadecimal string
      */
     @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
     public void addPollingLoopFilterToAutoTransact(@NonNull String pollingLoopFilter) {
-        mPollingLoopFilters.add(pollingLoopFilter.toUpperCase(Locale.ROOT));
         mAutoTransact.put(pollingLoopFilter.toUpperCase(Locale.ROOT), true);
     }
 
@@ -702,7 +702,7 @@
      */
     @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
     public void removePollingLoopFilter(@NonNull String pollingLoopFilter) {
-        mPollingLoopFilters.remove(pollingLoopFilter.toUpperCase(Locale.ROOT));
+        mAutoTransact.remove(pollingLoopFilter.toUpperCase(Locale.ROOT));
     }
 
     /**
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/ListPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/ListPreference.kt
index a0149da..1a04bb8 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/ListPreference.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/ListPreference.kt
@@ -37,11 +37,13 @@
 import androidx.compose.ui.semantics.Role
 import com.android.settingslib.spa.framework.theme.SettingsDimension
 import com.android.settingslib.spa.widget.dialog.SettingsDialog
+import com.android.settingslib.spa.widget.ui.SettingsBody
 import com.android.settingslib.spa.widget.ui.SettingsDialogItem
 
 data class ListPreferenceOption(
     val id: Int,
     val text: String,
+    val summary: String = String()
 )
 
 /**
@@ -129,6 +131,14 @@
     ) {
         RadioButton(selected = selected, onClick = null, enabled = enabled)
         Spacer(modifier = Modifier.width(SettingsDimension.itemPaddingEnd))
-        SettingsDialogItem(text = option.text, enabled = enabled)
+        Column {
+            SettingsDialogItem(text = option.text, enabled = enabled)
+            if (option.summary != String()) {
+                SettingsBody(
+                    body = option.summary,
+                    maxLines = 1
+                )
+            }
+        }
     }
 }
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetPreference.kt
index e36572f..3216e37 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetPreference.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetPreference.kt
@@ -24,7 +24,6 @@
 import androidx.compose.foundation.layout.size
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.remember
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.unit.dp
@@ -46,14 +45,14 @@
         verticalAlignment = Alignment.CenterVertically,
     ) {
         Box(modifier = Modifier.weight(1f)) {
-            Preference(remember {
+            Preference(
                 object : PreferenceModel {
                     override val title = title
                     override val summary = summary
                     override val icon = icon
                     override val onClick = onClick
                 }
-            })
+            )
         }
         PreferenceDivider()
         widget()
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/ListPreferenceTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/ListPreferenceTest.kt
index 796ac48..417ce6e 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/ListPreferenceTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/ListPreferenceTest.kt
@@ -123,6 +123,26 @@
     }
 
     @Test
+    fun click_optionsNotEmptyAndItemHasSummary_itemShowSummary() {
+        composeTestRule.setContent {
+            ListPreference(remember {
+                object : ListPreferenceModel {
+                    override val title = TITLE
+                    override val options =
+                        listOf(ListPreferenceOption(id = 1, text = "A", summary = "A_Summary"))
+                    override val selectedId = mutableIntStateOf(1)
+                    override val onIdSelected: (Int) -> Unit = {}
+                }
+            })
+        }
+
+        composeTestRule.onNodeWithText(TITLE).performClick()
+
+        composeTestRule.onDialogText(TITLE).assertIsDisplayed()
+        composeTestRule.onNodeWithText("A_Summary").assertIsDisplayed()
+    }
+
+    @Test
     fun select() {
         val selectedId = mutableIntStateOf(1)
         composeTestRule.setContent {
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index 249fa7f..e489bc5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -1702,7 +1702,8 @@
         }
 
         public boolean isPrivateProfile() {
-            return UserManager.USER_TYPE_PROFILE_PRIVATE.equals(mProfileType);
+            return android.os.Flags.allowPrivateProfile()
+                    && UserManager.USER_TYPE_PROFILE_PRIVATE.equals(mProfileType);
         }
 
         /**
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java
index 213a66e..1ad7d49 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java
@@ -22,21 +22,30 @@
 import static org.mockito.Mockito.when;
 
 import android.content.pm.ApplicationInfo;
+import android.os.Flags;
 import android.os.UserManager;
+import android.platform.test.flag.junit.SetFlagsRule;
+
+import androidx.test.core.app.ApplicationProvider;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
 @RunWith(JUnit4.class)
 public class ApplicationsStateTest {
+    private static final int APP_ENTRY_ID = 1;
     private ApplicationsState.AppEntry mEntry;
+    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
     @Before
     public void setUp() {
-        mEntry = mock(ApplicationsState.AppEntry.class);
-        mEntry.info = mock(ApplicationInfo.class);
+        mEntry = new ApplicationsState.AppEntry(
+                ApplicationProvider.getApplicationContext(),
+                mock(ApplicationInfo.class),
+                APP_ENTRY_ID);
     }
 
     @Test
@@ -310,6 +319,8 @@
 
     @Test
     public void testPrivateProfileFilterDisplaysCorrectApps() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+
         mEntry.showInPersonalTab = true;
         mEntry.mProfileType = UserManager.USER_TYPE_FULL_SYSTEM;
         assertThat(ApplicationsState.FILTER_PERSONAL.filterApp(mEntry)).isTrue();
@@ -320,4 +331,14 @@
         assertThat(ApplicationsState.FILTER_PERSONAL.filterApp(mEntry)).isFalse();
         assertThat(ApplicationsState.FILTER_PRIVATE_PROFILE.filterApp(mEntry)).isTrue();
     }
+
+    @Test
+    public void testPrivateProfileFilterDisplaysCorrectAppsWhenFlagDisabled() {
+        mSetFlagsRule.disableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+
+        mEntry.showInPersonalTab = false;
+        mEntry.mProfileType = UserManager.USER_TYPE_PROFILE_PRIVATE;
+        assertThat(ApplicationsState.FILTER_PERSONAL.filterApp(mEntry)).isFalse();
+        assertThat(ApplicationsState.FILTER_PRIVATE_PROFILE.filterApp(mEntry)).isFalse();
+    }
 }
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt
index e4dc9be..5d5f12e 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt
@@ -123,7 +123,7 @@
             val views = LinkedList<View>().apply { add(view) }
 
             while (views.isNotEmpty()) {
-                val v = views.removeFirst()
+                val v = views.removeAt(0)
                 if (v.background != null) {
                     return v.background
                 }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
index 828e34d..2e781e6 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
@@ -29,13 +29,17 @@
 import androidx.compose.ui.graphics.CompositingStrategy
 import androidx.compose.ui.graphics.drawscope.ContentDrawScope
 import androidx.compose.ui.graphics.drawscope.scale
-import androidx.compose.ui.layout.IntermediateMeasureScope
+import androidx.compose.ui.layout.ApproachLayoutModifierNode
+import androidx.compose.ui.layout.ApproachMeasureScope
+import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.layout.LookaheadScope
 import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.layout.MeasureResult
 import androidx.compose.ui.layout.Placeable
-import androidx.compose.ui.layout.intermediateLayout
 import androidx.compose.ui.node.DrawModifierNode
 import androidx.compose.ui.node.ModifierNodeElement
 import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.testTag
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.round
@@ -91,23 +95,7 @@
     layoutImpl: SceneTransitionLayoutImpl,
     scene: Scene,
     key: ElementKey,
-): Modifier {
-    return this.then(ElementModifier(layoutImpl, scene, key))
-        // TODO(b/311132415): Move this into ElementNode once we can create a delegate
-        // IntermediateLayoutModifierNode.
-        .intermediateLayout { measurable, constraints ->
-            // TODO(b/311132415): No need to fetch the element and sceneState from the map anymore
-            // once this is merged into ElementNode.
-            val element = layoutImpl.elements.getValue(key)
-            val sceneState = element.sceneStates.getValue(scene.key)
-
-            val placeable = measure(layoutImpl, scene, element, sceneState, measurable, constraints)
-            layout(placeable.width, placeable.height) {
-                place(layoutImpl, scene, element, sceneState, placeable, placementScope = this)
-            }
-        }
-        .testTag(key.testTag)
-}
+): Modifier = this.then(ElementModifier(layoutImpl, scene, key)).testTag(key.testTag)
 
 /**
  * An element associated to [ElementNode]. Note that this element does not support updates as its
@@ -129,7 +117,7 @@
     private var layoutImpl: SceneTransitionLayoutImpl,
     private var scene: Scene,
     private var key: ElementKey,
-) : Modifier.Node(), DrawModifierNode {
+) : Modifier.Node(), DrawModifierNode, ApproachLayoutModifierNode {
     private var _element: Element? = null
     private val element: Element
         get() = _element!!
@@ -197,6 +185,31 @@
         maybePruneMaps(layoutImpl, prevElement, prevSceneState)
     }
 
+    override fun isMeasurementApproachComplete(lookaheadSize: IntSize): Boolean {
+        // TODO(b/324191441): Investigate whether making this check more complex (checking if this
+        // element is shared or transformed) would lead to better performance.
+        return layoutImpl.state.currentTransition == null
+    }
+
+    override fun Placeable.PlacementScope.isPlacementApproachComplete(
+        lookaheadCoordinates: LayoutCoordinates
+    ): Boolean {
+        // TODO(b/324191441): Investigate whether making this check more complex (checking if this
+        // element is shared or transformed) would lead to better performance.
+        return layoutImpl.state.currentTransition == null
+    }
+
+    @ExperimentalComposeUiApi
+    override fun ApproachMeasureScope.approachMeasure(
+        measurable: Measurable,
+        constraints: Constraints,
+    ): MeasureResult {
+        val placeable = measure(layoutImpl, scene, element, sceneState, measurable, constraints)
+        return layout(placeable.width, placeable.height) {
+            place(layoutImpl, scene, element, sceneState, placeable, placementScope = this)
+        }
+    }
+
     override fun ContentDrawScope.draw() {
         val drawScale = getDrawScale(layoutImpl, element, scene)
         if (drawScale == Scale.Default) {
@@ -368,7 +381,7 @@
 }
 
 @OptIn(ExperimentalComposeUiApi::class)
-private fun IntermediateMeasureScope.measure(
+private fun ApproachMeasureScope.measure(
     layoutImpl: SceneTransitionLayoutImpl,
     scene: Scene,
     element: Element,
@@ -431,7 +444,7 @@
 }
 
 @OptIn(ExperimentalComposeUiApi::class)
-private fun IntermediateMeasureScope.place(
+private fun ApproachMeasureScope.place(
     layoutImpl: SceneTransitionLayoutImpl,
     scene: Scene,
     element: Element,
@@ -439,6 +452,8 @@
     placeable: Placeable,
     placementScope: Placeable.PlacementScope,
 ) {
+    this as LookaheadScope
+
     with(placementScope) {
         // Update the offset (relative to the SceneTransitionLayout) this element has in this scene
         // when idle.
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
index 1cdba2d..662f33f 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
@@ -154,7 +154,7 @@
     stateLinks: List<StateLink> = emptyList(),
 ): SceneTransitionLayoutState {
     return remember {
-            HoistedSceneTransitionLayoutScene(
+            HoistedSceneTransitionLayoutState(
                 currentScene,
                 transitions,
                 onChangeScene,
@@ -364,7 +364,7 @@
  * A [SceneTransitionLayout] whose current scene/source of truth is hoisted (its current value comes
  * from outside).
  */
-internal class HoistedSceneTransitionLayoutScene(
+internal class HoistedSceneTransitionLayoutState(
     initialScene: SceneKey,
     override var transitions: SceneTransitions,
     private var changeScene: (SceneKey) -> Unit,
@@ -400,7 +400,7 @@
                 // late.
                 val newKey = targetSceneChannel.tryReceive().getOrNull() ?: newKey
                 animateToScene(
-                    layoutState = this@HoistedSceneTransitionLayoutScene,
+                    layoutState = this@HoistedSceneTransitionLayoutState,
                     target = newKey,
                     transitionKey = null,
                 )
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
index c9b5b75..33be1dc 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
@@ -44,7 +44,6 @@
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.test.runTest
 import org.junit.Assert.assertThrows
-import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -248,11 +247,9 @@
     }
 
     @Test
-    @Ignore
-    fun elementIsReusedInSameSceneAndBetweenScenes() {
+    fun elementIsReusedBetweenScenes() {
         var currentScene by mutableStateOf(TestScenes.SceneA)
         var sceneCState by mutableStateOf(0)
-        var sceneDState by mutableStateOf(0)
         val key = TestElements.Foo
         var nullableLayoutImpl: SceneTransitionLayoutImpl? = null
 
@@ -270,19 +267,6 @@
                 scene(TestScenes.SceneC) {
                     when (sceneCState) {
                         0 -> Row(Modifier.element(key)) {}
-                        1 -> Column(Modifier.element(key)) {}
-                        else -> {
-                            /* Nothing */
-                        }
-                    }
-                }
-                scene(TestScenes.SceneD) {
-                    // We should be able to extract the modifier before assigning it to different
-                    // nodes.
-                    val childModifier = Modifier.element(key)
-                    when (sceneDState) {
-                        0 -> Row(childModifier) {}
-                        1 -> Column(childModifier) {}
                         else -> {
                             /* Nothing */
                         }
@@ -315,35 +299,10 @@
         assertThat(layoutImpl.elements.getValue(key)).isSameInstanceAs(element)
         assertThat(element.sceneStates.keys).containsExactly(TestScenes.SceneC)
 
-        // Scene C, state 1: the same element is reused.
+        // Scene C, state 1: the element is removed from the map.
         sceneCState = 1
         rule.waitForIdle()
 
-        assertThat(layoutImpl.elements.keys).containsExactly(key)
-        assertThat(layoutImpl.elements.getValue(key)).isSameInstanceAs(element)
-        assertThat(element.sceneStates.keys).containsExactly(TestScenes.SceneC)
-
-        // Scene D, state 0: the same element is reused.
-        currentScene = TestScenes.SceneD
-        sceneDState = 0
-        rule.waitForIdle()
-
-        assertThat(layoutImpl.elements.keys).containsExactly(key)
-        assertThat(layoutImpl.elements.getValue(key)).isSameInstanceAs(element)
-        assertThat(element.sceneStates.keys).containsExactly(TestScenes.SceneD)
-
-        // Scene D, state 1: the same element is reused.
-        sceneDState = 1
-        rule.waitForIdle()
-
-        assertThat(layoutImpl.elements.keys).containsExactly(key)
-        assertThat(layoutImpl.elements.getValue(key)).isSameInstanceAs(element)
-        assertThat(element.sceneStates.keys).containsExactly(TestScenes.SceneD)
-
-        // Scene D, state 2: the element is removed from the map.
-        sceneDState = 2
-        rule.waitForIdle()
-
         assertThat(element.sceneStates).isEmpty()
         assertThat(layoutImpl.elements).isEmpty()
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModelTest.kt
index 837a9db..d33c10e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModelTest.kt
@@ -20,6 +20,7 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.systemui.Flags as AConfigFlags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
@@ -66,6 +67,25 @@
     }
 
     @Test
+    fun alpha_WhenNotGone_clockMigrationFlagIsOff_emitsKeyguardAlpha() =
+        testScope.runTest {
+            mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
+            val alpha by collectLastValue(underTest.alpha)
+
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.AOD,
+                to = KeyguardState.LOCKSCREEN,
+                testScope = testScope,
+            )
+
+            keyguardRepository.setKeyguardAlpha(0.5f)
+            assertThat(alpha).isEqualTo(0.5f)
+
+            keyguardRepository.setKeyguardAlpha(0.8f)
+            assertThat(alpha).isEqualTo(0.8f)
+        }
+
+    @Test
     fun alpha_WhenGoneToAod() =
         testScope.runTest {
             val alpha by collectLastValue(underTest.alpha)
@@ -112,6 +132,7 @@
     @Test
     fun alpha_whenGone_equalsZero() =
         testScope.runTest {
+            mSetFlagsRule.enableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
             val alpha by collectLastValue(underTest.alpha)
 
             keyguardTransitionRepository.sendTransitionStep(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerMainThreadTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerMainThreadTest.java
deleted file mode 100644
index d0e05fa..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerMainThreadTest.java
+++ /dev/null
@@ -1,614 +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.systemui.statusbar;
-
-import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
-import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS;
-import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS;
-import static android.provider.Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS;
-import static android.provider.Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS;
-
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.ActivityManager;
-import android.app.KeyguardManager;
-import android.app.Notification;
-import android.app.admin.DevicePolicyManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.UserInfo;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.provider.Settings;
-import android.util.SparseArray;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.widget.LockPatternUtils;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FakeFeatureFlagsClassic;
-import com.android.systemui.flags.Flags;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.recents.OverviewProxyService;
-import com.android.systemui.settings.UserTracker;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager.NotificationStateChangedListener;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
-import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
-import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.util.settings.FakeSettings;
-
-import com.google.android.collect.Lists;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.concurrent.Executor;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class NotificationLockscreenUserManagerMainThreadTest extends SysuiTestCase {
-    @Mock
-    private NotificationPresenter mPresenter;
-    @Mock
-    private UserManager mUserManager;
-    @Mock
-    private UserTracker mUserTracker;
-
-    // Dependency mocks:
-    @Mock
-    private NotificationVisibilityProvider mVisibilityProvider;
-    @Mock
-    private CommonNotifCollection mNotifCollection;
-    @Mock
-    private DevicePolicyManager mDevicePolicyManager;
-    @Mock
-    private NotificationClickNotifier mClickNotifier;
-    @Mock
-    private OverviewProxyService mOverviewProxyService;
-    @Mock
-    private KeyguardManager mKeyguardManager;
-    @Mock
-    private DeviceProvisionedController mDeviceProvisionedController;
-    @Mock
-    private StatusBarStateController mStatusBarStateController;
-    @Mock
-    private BroadcastDispatcher mBroadcastDispatcher;
-    @Mock
-    private KeyguardStateController mKeyguardStateController;
-
-    private UserInfo mCurrentUser;
-    private UserInfo mSecondaryUser;
-    private UserInfo mWorkUser;
-    private UserInfo mCommunalUser;
-    private FakeSettings mSettings;
-    private TestNotificationLockscreenUserManager mLockscreenUserManager;
-    private NotificationEntry mCurrentUserNotif;
-    private NotificationEntry mSecondaryUserNotif;
-    private NotificationEntry mWorkProfileNotif;
-    private final FakeFeatureFlagsClassic mFakeFeatureFlags = new FakeFeatureFlagsClassic();
-    private Executor mMainExecutor = Runnable::run; // Direct executor
-    private Executor mBackgroundExecutor = Runnable::run; // Direct executor
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-
-        mFakeFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, false);
-
-        int currentUserId = ActivityManager.getCurrentUser();
-        when(mUserTracker.getUserId()).thenReturn(currentUserId);
-        mSettings = new FakeSettings();
-        mSettings.setUserId(ActivityManager.getCurrentUser());
-        mCurrentUser = new UserInfo(currentUserId, "", 0);
-        mSecondaryUser = new UserInfo(currentUserId + 1, "", 0);
-        mWorkUser = new UserInfo(currentUserId + 2, "" /* name */, null /* iconPath */, 0,
-                UserManager.USER_TYPE_PROFILE_MANAGED);
-        mCommunalUser = new UserInfo(currentUserId + 3, "" /* name */, null /* iconPath */, 0,
-                UserManager.USER_TYPE_PROFILE_COMMUNAL);
-
-        when(mKeyguardManager.getPrivateNotificationsAllowed()).thenReturn(true);
-        when(mUserManager.getProfiles(currentUserId)).thenReturn(Lists.newArrayList(
-                mCurrentUser, mWorkUser));
-        when(mUserManager.getProfilesIncludingCommunal(currentUserId)).thenReturn(
-                Lists.newArrayList(mCurrentUser, mWorkUser, mCommunalUser));
-        when(mUserManager.getProfiles(mSecondaryUser.id)).thenReturn(Lists.newArrayList(
-                mSecondaryUser));
-        when(mUserManager.getProfilesIncludingCommunal(mSecondaryUser.id)).thenReturn(
-                Lists.newArrayList(mSecondaryUser, mCommunalUser));
-
-        Notification notifWithPrivateVisibility = new Notification();
-        notifWithPrivateVisibility.visibility = Notification.VISIBILITY_PRIVATE;
-        mCurrentUserNotif = new NotificationEntryBuilder()
-                .setNotification(notifWithPrivateVisibility)
-                .setUser(new UserHandle(mCurrentUser.id))
-                .build();
-        mSecondaryUserNotif = new NotificationEntryBuilder()
-                .setNotification(notifWithPrivateVisibility)
-                .setUser(new UserHandle(mSecondaryUser.id))
-                .build();
-        mWorkProfileNotif = new NotificationEntryBuilder()
-                .setNotification(notifWithPrivateVisibility)
-                .setUser(new UserHandle(mWorkUser.id))
-                .build();
-
-        mLockscreenUserManager = new TestNotificationLockscreenUserManager(mContext);
-        mLockscreenUserManager.setUpWithPresenter(mPresenter);
-    }
-
-    private void changeSetting(String setting) {
-        final Collection<Uri> lockScreenUris = new ArrayList<>();
-        lockScreenUris.add(Settings.Secure.getUriFor(setting));
-        mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false,
-            lockScreenUris, 0);
-    }
-
-    @Test
-    public void testGetCurrentProfiles() {
-        final SparseArray<UserInfo> expectedCurProfiles = new SparseArray<>();
-        expectedCurProfiles.put(mCurrentUser.id, mCurrentUser);
-        expectedCurProfiles.put(mWorkUser.id, mWorkUser);
-        if (android.multiuser.Flags.supportCommunalProfile()) {
-            expectedCurProfiles.put(mCommunalUser.id, mCommunalUser);
-        }
-        assertTrue(mLockscreenUserManager.getCurrentProfiles().contentEquals(expectedCurProfiles));
-
-        mLockscreenUserManager.mUserChangedCallback.onUserChanging(mSecondaryUser.id, mContext);
-
-        final SparseArray<UserInfo> expectedSecProfiles = new SparseArray<>();
-        expectedSecProfiles.put(mSecondaryUser.id, mSecondaryUser);
-        if (android.multiuser.Flags.supportCommunalProfile()) {
-            expectedSecProfiles.put(mCommunalUser.id, mCommunalUser);
-        }
-        assertTrue(mLockscreenUserManager.getCurrentProfiles().contentEquals(expectedSecProfiles));
-    }
-
-    @Test
-    public void testLockScreenShowNotificationsFalse() {
-        mSettings.putInt(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0);
-        changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
-        assertFalse(mLockscreenUserManager.shouldShowLockscreenNotifications());
-    }
-
-    @Test
-    public void testLockScreenShowNotificationsTrue() {
-        mSettings.putInt(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
-        changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
-        assertTrue(mLockscreenUserManager.shouldShowLockscreenNotifications());
-    }
-
-    @Test
-    public void testLockScreenAllowPrivateNotificationsTrue() {
-        mSettings.putInt(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1);
-        changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
-        assertTrue(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mCurrentUser.id));
-    }
-
-    @Test
-    public void testLockScreenAllowPrivateNotificationsFalse() {
-        mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
-                mCurrentUser.id);
-        changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
-        assertFalse(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mCurrentUser.id));
-    }
-
-    @Test
-    public void testLockScreenAllowsWorkPrivateNotificationsFalse() {
-        mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
-                mWorkUser.id);
-        changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
-        assertFalse(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mWorkUser.id));
-    }
-
-    @Test
-    public void testLockScreenAllowsWorkPrivateNotificationsTrue() {
-        mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
-                mWorkUser.id);
-        changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
-        assertTrue(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mWorkUser.id));
-    }
-
-    @Test
-    public void testCurrentUserPrivateNotificationsNotRedacted() {
-        // GIVEN current user doesn't allow private notifications to show
-        mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
-                mCurrentUser.id);
-        changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
-
-        // THEN current user's notification is redacted
-        assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
-    }
-
-    @Test
-    public void testCurrentUserPrivateNotificationsRedacted() {
-        // GIVEN current user allows private notifications to show
-        mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
-                mCurrentUser.id);
-        changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
-
-        // THEN current user's notification isn't redacted
-        assertFalse(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
-    }
-
-    @Test
-    public void testWorkPrivateNotificationsRedacted() {
-        // GIVEN work profile doesn't private notifications to show
-        mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
-                mWorkUser.id);
-        changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
-
-        // THEN work profile notification is redacted
-        assertTrue(mLockscreenUserManager.needsRedaction(mWorkProfileNotif));
-        assertFalse(mLockscreenUserManager.allowsManagedPrivateNotificationsInPublic());
-    }
-
-    @Test
-    public void testWorkPrivateNotificationsNotRedacted() {
-        // GIVEN work profile allows private notifications to show
-        mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
-                mWorkUser.id);
-        changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
-
-        // THEN work profile notification isn't redacted
-        assertFalse(mLockscreenUserManager.needsRedaction(mWorkProfileNotif));
-        assertTrue(mLockscreenUserManager.allowsManagedPrivateNotificationsInPublic());
-    }
-
-    @Test
-    public void testWorkPrivateNotificationsNotRedacted_otherUsersRedacted() {
-        // GIVEN work profile allows private notifications to show but the other users don't
-        mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
-                mWorkUser.id);
-        mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
-                mCurrentUser.id);
-        mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
-                mSecondaryUser.id);
-        changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
-        changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
-        changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
-
-        // THEN the work profile notification doesn't need to be redacted
-        assertFalse(mLockscreenUserManager.needsRedaction(mWorkProfileNotif));
-
-        // THEN the current user and secondary user notifications do need to be redacted
-        assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
-        assertTrue(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif));
-    }
-
-    @Test
-    public void testWorkProfileRedacted_otherUsersNotRedacted() {
-        // GIVEN work profile doesn't allow private notifications to show but the other users do
-        mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
-                mWorkUser.id);
-        mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
-                mCurrentUser.id);
-        mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
-                mSecondaryUser.id);
-        changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
-        changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
-        changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
-
-        // THEN the work profile notification needs to be redacted
-        assertTrue(mLockscreenUserManager.needsRedaction(mWorkProfileNotif));
-
-        // THEN the current user and secondary user notifications don't need to be redacted
-        assertFalse(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
-        assertFalse(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif));
-    }
-
-    @Test
-    public void testSecondaryUserNotRedacted_currentUserRedacted() {
-        // GIVEN secondary profile allows private notifications to show but the current user
-        // doesn't allow private notifications to show
-        mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
-                mCurrentUser.id);
-        mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
-                mSecondaryUser.id);
-        changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
-        changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
-
-        // THEN the secondary profile notification still needs to be redacted because the current
-        // user's setting takes precedence
-        assertTrue(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif));
-    }
-
-    @Test
-    public void testUserSwitchedCallsOnUserSwitching() {
-        mLockscreenUserManager.getUserTrackerCallbackForTest().onUserChanging(mSecondaryUser.id,
-                mContext);
-        verify(mPresenter, times(1)).onUserSwitched(mSecondaryUser.id);
-    }
-
-    @Test
-    public void testIsLockscreenPublicMode() {
-        assertFalse(mLockscreenUserManager.isLockscreenPublicMode(mCurrentUser.id));
-        mLockscreenUserManager.setLockscreenPublicMode(true, mCurrentUser.id);
-        assertTrue(mLockscreenUserManager.isLockscreenPublicMode(mCurrentUser.id));
-    }
-
-    @Test
-    public void testUpdateIsPublicMode() {
-        when(mKeyguardStateController.isMethodSecure()).thenReturn(true);
-
-        NotificationStateChangedListener listener = mock(NotificationStateChangedListener.class);
-        mLockscreenUserManager.addNotificationStateChangedListener(listener);
-        mLockscreenUserManager.mCurrentProfiles.append(0, mock(UserInfo.class));
-
-        // first call explicitly sets user 0 to not public; notifies
-        mLockscreenUserManager.updatePublicMode();
-        assertFalse(mLockscreenUserManager.isLockscreenPublicMode(0));
-        verify(listener).onNotificationStateChanged();
-        clearInvocations(listener);
-
-        // calling again has no changes; does not notify
-        mLockscreenUserManager.updatePublicMode();
-        assertFalse(mLockscreenUserManager.isLockscreenPublicMode(0));
-        verify(listener, never()).onNotificationStateChanged();
-
-        // Calling again with keyguard now showing makes user 0 public; notifies
-        when(mKeyguardStateController.isShowing()).thenReturn(true);
-        mLockscreenUserManager.updatePublicMode();
-        assertTrue(mLockscreenUserManager.isLockscreenPublicMode(0));
-        verify(listener).onNotificationStateChanged();
-        clearInvocations(listener);
-
-        // calling again has no changes; does not notify
-        mLockscreenUserManager.updatePublicMode();
-        assertTrue(mLockscreenUserManager.isLockscreenPublicMode(0));
-        verify(listener, never()).onNotificationStateChanged();
-    }
-
-    @Test
-    public void testDevicePolicyDoesNotAllowNotifications() {
-        // User allows them
-        mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
-        changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
-
-        // DevicePolicy hides notifs on lockscreen
-        when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id))
-                .thenReturn(KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
-
-        BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult(
-                0, null, null, 0, true, false, null, mCurrentUser.id, 0);
-        mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
-        mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
-                new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
-
-        assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id));
-    }
-
-    @Test
-    public void testDevicePolicyDoesNotAllowNotifications_secondary() {
-        Mockito.clearInvocations(mDevicePolicyManager);
-        // User allows notifications
-        mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mSecondaryUser.id);
-        changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
-
-        // DevicePolicy hides notifications
-        when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mSecondaryUser.id))
-                .thenReturn(KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
-
-        BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult(
-                0, null, null, 0, true, false, null, mSecondaryUser.id, 0);
-        mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
-        mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
-                new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
-
-        assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mSecondaryUser.id));
-    }
-
-    @Test
-    public void testDevicePolicy_noPrivateNotifications() {
-        Mockito.clearInvocations(mDevicePolicyManager);
-        // User allows notifications
-        mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
-        changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
-
-        // DevicePolicy hides sensitive content
-        when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id))
-                .thenReturn(KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
-
-        BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult(
-                0, null, null, 0, true, false, null, mCurrentUser.id, 0);
-        mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
-        mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
-                new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
-
-        assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
-    }
-
-    @Test
-    public void testDevicePolicy_noPrivateNotifications_userAll() {
-        // User allows notifications
-        mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
-        changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
-
-        // DevicePolicy hides sensitive content
-        when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id))
-                .thenReturn(KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
-
-        BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult(
-                0, null, null, 0, true, false, null, mCurrentUser.id, 0);
-        mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
-        mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
-                new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
-
-        assertTrue(mLockscreenUserManager.needsRedaction(new NotificationEntryBuilder()
-                .setNotification(new Notification())
-                .setUser(UserHandle.ALL)
-                .build()));
-    }
-
-    @Test
-    public void testDevicePolicyPrivateNotifications_secondary() {
-        Mockito.clearInvocations(mDevicePolicyManager);
-        // User allows notifications
-        mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mSecondaryUser.id);
-        changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
-
-        // DevicePolicy hides sensitive content
-        when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mSecondaryUser.id))
-                .thenReturn(KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
-
-        BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult(
-                0, null, null, 0, true, false, null, mSecondaryUser.id, 0);
-        mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
-        mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
-                new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
-
-        mLockscreenUserManager.mUserChangedCallback.onUserChanging(mSecondaryUser.id, mContext);
-        assertTrue(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif));
-    }
-
-    @Test
-    public void testHideNotifications_primary() {
-        mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, mCurrentUser.id);
-        changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
-
-        assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id));
-    }
-
-    @Test
-    public void testHideNotifications_secondary() {
-        mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, mSecondaryUser.id);
-        changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
-
-        assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mSecondaryUser.id));
-    }
-
-    @Test
-    public void testHideNotifications_secondary_userSwitch() {
-        mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, mSecondaryUser.id);
-        changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
-
-        mLockscreenUserManager.mUserChangedCallback.onUserChanging(mSecondaryUser.id, mContext);
-
-        assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mSecondaryUser.id));
-    }
-
-    @Test
-    public void testShowNotifications_secondary_userSwitch() {
-        mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mSecondaryUser.id);
-        changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
-
-        mLockscreenUserManager.mUserChangedCallback.onUserChanging(mSecondaryUser.id, mContext);
-
-        assertTrue(mLockscreenUserManager.userAllowsNotificationsInPublic(mSecondaryUser.id));
-    }
-
-    @Test
-    public void testUserAllowsNotificationsInPublic_keyguardManagerNoPrivateNotifications() {
-        // DevicePolicy allows notifications
-        when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id))
-                .thenReturn(0);
-        BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult(
-                0, null, null, 0, true, false, null, mCurrentUser.id, 0);
-        mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
-        mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
-                new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
-
-        // KeyguardManager does not allow notifications
-        when(mKeyguardManager.getPrivateNotificationsAllowed()).thenReturn(false);
-
-        // User allows notifications
-        mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
-        // We shouldn't need to call this method, but getPrivateNotificationsAllowed has no
-        // callback, so it's only updated when the setting is
-        changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
-
-        assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id));
-    }
-
-    @Test
-    public void testUserAllowsNotificationsInPublic_settingsChange() {
-        // User allows notifications
-        mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
-        changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
-
-        assertTrue(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id));
-
-        // User disables
-        mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, mCurrentUser.id);
-        changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
-
-        assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id));
-    }
-
-    private class TestNotificationLockscreenUserManager
-            extends NotificationLockscreenUserManagerImpl {
-        public TestNotificationLockscreenUserManager(Context context) {
-            super(
-                    context,
-                    mBroadcastDispatcher,
-                    mDevicePolicyManager,
-                    mUserManager,
-                    mUserTracker,
-                    (() -> mVisibilityProvider),
-                    (() -> mNotifCollection),
-                    mClickNotifier,
-                    (() -> mOverviewProxyService),
-                    NotificationLockscreenUserManagerMainThreadTest.this.mKeyguardManager,
-                    mStatusBarStateController,
-                    mMainExecutor,
-                    mBackgroundExecutor,
-                    mDeviceProvisionedController,
-                    mKeyguardStateController,
-                    mSettings,
-                    mock(DumpManager.class),
-                    mock(LockPatternUtils.class),
-                    mFakeFeatureFlags);
-        }
-
-        public BroadcastReceiver getBaseBroadcastReceiverForTest() {
-            return mBaseBroadcastReceiver;
-        }
-
-        public UserTracker.Callback getUserTrackerCallbackForTest() {
-            return mUserChangedCallback;
-        }
-
-        public ContentObserver getLockscreenSettingsObserverForTest() {
-            return mLockscreenSettingsObserver;
-        }
-
-        public ContentObserver getSettingsObserverForTest() {
-            return mSettingsObserver;
-        }
-    }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index bcc0710..d505b27 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -172,8 +172,6 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
 
-        mFakeFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, true);
-
         int currentUserId = ActivityManager.getCurrentUser();
         when(mUserTracker.getUserId()).thenReturn(currentUserId);
         mSettings = new FakeSettings();
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 6eff792..56162ae 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -102,11 +102,6 @@
             default = true
         )
 
-    // TODO(b/301955929)
-    @JvmField
-    val NOTIF_LS_BACKGROUND_THREAD =
-            releasedFlag("notification_lockscreen_mgr_bg_thread")
-
     // 200 - keyguard/lockscreen
     // ** Flag retired **
     // public static final BooleanFlag KEYGUARD_LAYOUT =
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/shared/model/StickyKey.kt b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/shared/model/StickyKey.kt
index 72a81cb..0f1cc99 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/shared/model/StickyKey.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/shared/model/StickyKey.kt
@@ -20,8 +20,8 @@
 value class Locked(val locked: Boolean)
 
 enum class ModifierKey(val displayedText: String) {
-    ALT("ALT LEFT"),
-    ALT_GR("ALT RIGHT"),
+    ALT("ALT"),
+    ALT_GR("ALT"),
     CTRL("CTRL"),
     META("ACTION"),
     SHIFT("SHIFT"),
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModel.kt
index f208e85..8a3b57b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModel.kt
@@ -18,7 +18,9 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import com.android.systemui.Flags.migrateClocksToBlueprint
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
 import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
@@ -38,6 +40,7 @@
     keyguardTransitionInteractor: KeyguardTransitionInteractor,
     goneToAodTransitionViewModel: GoneToAodTransitionViewModel,
     goneToDozingTransitionViewModel: GoneToDozingTransitionViewModel,
+    keyguardInteractor: KeyguardInteractor,
 ) {
 
     /** The alpha level for the entire lockscreen while in AOD. */
@@ -46,7 +49,8 @@
                 keyguardTransitionInteractor.transitions,
                 goneToAodTransitionViewModel.enterFromTopAnimationAlpha.onStart { emit(0f) },
                 goneToDozingTransitionViewModel.lockscreenAlpha.onStart { emit(0f) },
-            ) { step, goneToAodAlpha, goneToDozingAlpha ->
+                keyguardInteractor.keyguardAlpha.onStart { emit(1f) },
+            ) { step, goneToAodAlpha, goneToDozingAlpha, keyguardAlpha ->
                 if (step.to == GONE) {
                     // When transitioning to GONE, only emit a value when complete as other
                     // transitions may be controlling the alpha fade
@@ -57,6 +61,8 @@
                     emit(goneToAodAlpha)
                 } else if (step.from == GONE && step.to == DOZING) {
                     emit(goneToDozingAlpha)
+                } else if (!migrateClocksToBlueprint()) {
+                    emit(keyguardAlpha)
                 }
             }
             .distinctUntilChanged()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index 9916ef6..1a06eec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -156,14 +156,12 @@
             final String action = intent.getAction();
 
             if (ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED.equals(action)) {
-                if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
-                    mKeyguardAllowingNotifications =
-                            intent.getBooleanExtra(EXTRA_KM_PRIVATE_NOTIFS_ALLOWED, false);
-                    if (mCurrentUserId == getSendingUserId()) {
-                        boolean changed = updateLockscreenNotificationSetting();
-                        if (changed) {
-                            notifyNotificationStateChanged();
-                        }
+                mKeyguardAllowingNotifications =
+                        intent.getBooleanExtra(EXTRA_KM_PRIVATE_NOTIFS_ALLOWED, false);
+                if (mCurrentUserId == getSendingUserId()) {
+                    boolean changed = updateLockscreenNotificationSetting();
+                    if (changed) {
+                        notifyNotificationStateChanged();
                     }
                 }
             }
@@ -176,36 +174,26 @@
             final String action = intent.getAction();
 
             if (ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(action)) {
-                if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
-                    boolean changed = false;
-                    int sendingUserId = getSendingUserId();
-                    if (sendingUserId == USER_ALL) {
-                        // When a Device Owner triggers changes it's sent as USER_ALL. Normalize
-                        // the user before calling into DPM
-                        sendingUserId = mCurrentUserId;
-                        @SuppressLint("MissingPermission")
-                        List<UserInfo> users = mUserManager.getUsers();
-                        for (int i = users.size() - 1; i >= 0; i--) {
-                            changed |= updateDpcSettings(users.get(i).id);
-                        }
-                    } else {
-                        changed |= updateDpcSettings(sendingUserId);
-                    }
-
-                    if (mCurrentUserId == sendingUserId) {
-                        changed |= updateLockscreenNotificationSetting();
-                    }
-                    if (changed) {
-                        notifyNotificationStateChanged();
+                boolean changed = false;
+                int sendingUserId = getSendingUserId();
+                if (sendingUserId == USER_ALL) {
+                    // When a Device Owner triggers changes it's sent as USER_ALL. Normalize
+                    // the user before calling into DPM
+                    sendingUserId = mCurrentUserId;
+                    @SuppressLint("MissingPermission")
+                    List<UserInfo> users = mUserManager.getUsers();
+                    for (int i = users.size() - 1; i >= 0; i--) {
+                        changed |= updateDpcSettings(users.get(i).id);
                     }
                 } else {
-                    if (isCurrentProfile(getSendingUserId())) {
-                        mUsersAllowingPrivateNotifications.clear();
-                        updateLockscreenNotificationSetting();
-                        // TODO(b/231976036): Consolidate pipeline invalidations related to this
-                        //  event
-                        // notifyNotificationStateChanged();
-                    }
+                    changed |= updateDpcSettings(sendingUserId);
+                }
+
+                if (mCurrentUserId == sendingUserId) {
+                    changed |= updateLockscreenNotificationSetting();
+                }
+                if (changed) {
+                    notifyNotificationStateChanged();
                 }
             }
         }
@@ -225,12 +213,10 @@
                 updateCurrentProfilesCache();
             } else if (Objects.equals(action, Intent.ACTION_USER_ADDED)){
                 updateCurrentProfilesCache();
-                if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
-                    final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
-                    mBackgroundExecutor.execute(() -> {
-                        initValuesForUser(userId);
-                    });
-                }
+                final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
+                mBackgroundExecutor.execute(() -> {
+                    initValuesForUser(userId);
+                });
             } else if (profileAvailabilityActions(action)) {
                 updateCurrentProfilesCache();
             } else if (Objects.equals(action, Intent.ACTION_USER_UNLOCKED)) {
@@ -360,28 +346,16 @@
     }
 
     private void init() {
-        mLockscreenSettingsObserver = new ExecutorContentObserver(
-                mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)
-                        ? mBackgroundExecutor
-                        : mMainExecutor) {
+        mLockscreenSettingsObserver = new ExecutorContentObserver(mBackgroundExecutor) {
 
             @Override
             public void onChange(boolean selfChange, Collection<Uri> uris, int flags) {
-                if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
-                    @SuppressLint("MissingPermission")
-                    List<UserInfo> users = mUserManager.getUsers();
-                    for (int i = users.size() - 1; i >= 0; i--) {
-                        onChange(selfChange, uris, flags,users.get(i).getUserHandle());
-                    }
-                } else {
-                    // We don't know which user changed LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS or
-                    // LOCK_SCREEN_SHOW_NOTIFICATIONS, so we just dump our cache ...
-                    mUsersAllowingPrivateNotifications.clear();
-                    mUsersAllowingNotifications.clear();
-                    // ... and refresh all the notifications
-                    updateLockscreenNotificationSetting();
-                    notifyNotificationStateChanged();
+                @SuppressLint("MissingPermission")
+                List<UserInfo> users = mUserManager.getUsers();
+                for (int i = users.size() - 1; i >= 0; i--) {
+                    onChange(selfChange, uris, flags,users.get(i).getUserHandle());
                 }
+
             }
 
             // Note: even though this is an override, this method is not called by the OS
@@ -390,22 +364,20 @@
             @Override
             public void onChange(boolean selfChange, Collection<Uri> uris,
                     int flags, UserHandle user) {
-                if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
-                    boolean changed = false;
-                    for (Uri uri: uris) {
-                        if (SHOW_LOCKSCREEN.equals(uri)) {
-                            changed |= updateUserShowSettings(user.getIdentifier());
-                        } else if (SHOW_PRIVATE_LOCKSCREEN.equals(uri)) {
-                            changed |= updateUserShowPrivateSettings(user.getIdentifier());
-                        }
+                boolean changed = false;
+                for (Uri uri: uris) {
+                    if (SHOW_LOCKSCREEN.equals(uri)) {
+                        changed |= updateUserShowSettings(user.getIdentifier());
+                    } else if (SHOW_PRIVATE_LOCKSCREEN.equals(uri)) {
+                        changed |= updateUserShowPrivateSettings(user.getIdentifier());
                     }
+                }
 
-                    if (mCurrentUserId == user.getIdentifier()) {
-                        changed |= updateLockscreenNotificationSetting();
-                    }
-                    if (changed) {
-                        notifyNotificationStateChanged();
-                    }
+                if (mCurrentUserId == user.getIdentifier()) {
+                    changed |= updateLockscreenNotificationSetting();
+                }
+                if (changed) {
+                    notifyNotificationStateChanged();
                 }
             }
         };
@@ -432,16 +404,10 @@
                 mLockscreenSettingsObserver,
                 USER_ALL);
 
-        if (!mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
-            mContext.getContentResolver().registerContentObserver(
-                    Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false,
-                    mSettingsObserver);
-        }
 
         mBroadcastDispatcher.registerReceiver(mAllUsersReceiver,
                 new IntentFilter(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
-                mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)
-                        ? mBackgroundExecutor : null, UserHandle.ALL);
+                mBackgroundExecutor, UserHandle.ALL);
         if (keyguardPrivateNotifications()) {
             mBroadcastDispatcher.registerReceiver(mKeyguardReceiver,
                     new IntentFilter(ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED),
@@ -471,17 +437,13 @@
         mCurrentUserId = mUserTracker.getUserId(); // in case we reg'd receiver too late
         updateCurrentProfilesCache();
 
-        if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
-            // Set  up
-            mBackgroundExecutor.execute(() -> {
-                @SuppressLint("MissingPermission") List<UserInfo> users = mUserManager.getUsers();
-                for (int i = users.size() - 1; i >= 0; i--) {
-                    initValuesForUser(users.get(i).id);
-                }
-            });
-        } else {
-            mSettingsObserver.onChange(false);  // set up
-        }
+        // Set  up
+        mBackgroundExecutor.execute(() -> {
+            @SuppressLint("MissingPermission") List<UserInfo> users = mUserManager.getUsers();
+            for (int i = users.size() - 1; i >= 0; i--) {
+                initValuesForUser(users.get(i).id);
+            }
+        });
     }
 
     private void initValuesForUser(@UserIdInt int userId) {
@@ -519,26 +481,15 @@
         boolean show;
         boolean allowedByDpm;
 
-        if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
-            if (keyguardPrivateNotifications()) {
-                show = mUsersUsersAllowingNotifications.get(mCurrentUserId);
-            } else {
-                show = mUsersUsersAllowingNotifications.get(mCurrentUserId)
-                        && mKeyguardAllowingNotifications;
-            }
-            // If DPC never notified us about a user, that means they have no policy for the user,
-            // and they allow the behavior
-            allowedByDpm = mUsersDpcAllowingNotifications.get(mCurrentUserId, true);
+        if (keyguardPrivateNotifications()) {
+            show = mUsersUsersAllowingNotifications.get(mCurrentUserId);
         } else {
-            show = mSecureSettings.getIntForUser(
-                    LOCK_SCREEN_SHOW_NOTIFICATIONS,
-                    1,
-                    mCurrentUserId) != 0;
-            final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(
-                    null /* admin */, mCurrentUserId);
-            allowedByDpm = (dpmFlags
-                    & KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) == 0;
+            show = mUsersUsersAllowingNotifications.get(mCurrentUserId)
+                    && mKeyguardAllowingNotifications;
         }
+        // If DPC never notified us about a user, that means they have no policy for the user,
+        // and they allow the behavior
+        allowedByDpm = mUsersDpcAllowingNotifications.get(mCurrentUserId, true);
 
         final boolean oldValue = mShowLockscreenNotifications;
         setShowLockscreenNotifications(show && allowedByDpm);
@@ -600,42 +551,24 @@
      * when the lockscreen is in "public" (secure & locked) mode?
      */
     public boolean userAllowsPrivateNotificationsInPublic(int userHandle) {
-        if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
-            if (userHandle == USER_ALL) {
-                userHandle = mCurrentUserId;
-            }
-            if (mUsersUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
-                Log.i(TAG, "Asking for redact notifs setting too early", new Throwable());
-                return false;
-            }
-            if (mUsersDpcAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
-                Log.i(TAG, "Asking for redact notifs dpm override too early", new Throwable());
-                return false;
-            }
-            if (keyguardPrivateNotifications()) {
-                return mUsersUsersAllowingPrivateNotifications.get(userHandle)
-                        && mUsersDpcAllowingPrivateNotifications.get(userHandle)
-                        && mKeyguardAllowingNotifications;
-            } else {
-                return mUsersUsersAllowingPrivateNotifications.get(userHandle)
-                        && mUsersDpcAllowingPrivateNotifications.get(userHandle);
-            }
+        if (userHandle == USER_ALL) {
+            userHandle = mCurrentUserId;
+        }
+        if (mUsersUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
+            Log.i(TAG, "Asking for redact notifs setting too early", new Throwable());
+            return false;
+        }
+        if (mUsersDpcAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
+            Log.i(TAG, "Asking for redact notifs dpm override too early", new Throwable());
+            return false;
+        }
+        if (keyguardPrivateNotifications()) {
+            return mUsersUsersAllowingPrivateNotifications.get(userHandle)
+                    && mUsersDpcAllowingPrivateNotifications.get(userHandle)
+                    && mKeyguardAllowingNotifications;
         } else {
-            if (userHandle == USER_ALL) {
-                return true;
-            }
-
-            if (mUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
-                final boolean allowedByUser = 0 != mSecureSettings.getIntForUser(
-                        LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, userHandle);
-                final boolean allowedByDpm = adminAllowsKeyguardFeature(userHandle,
-                        KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
-                final boolean allowed = allowedByUser && allowedByDpm;
-                mUsersAllowingPrivateNotifications.append(userHandle, allowed);
-                return allowed;
-            }
-
-            return mUsersAllowingPrivateNotifications.get(userHandle);
+            return mUsersUsersAllowingPrivateNotifications.get(userHandle)
+                    && mUsersDpcAllowingPrivateNotifications.get(userHandle);
         }
     }
 
@@ -688,48 +621,30 @@
      * "public" (secure & locked) mode?
      */
     public boolean userAllowsNotificationsInPublic(int userHandle) {
-        if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
-            // Unlike 'show private', settings does not show a copy of this setting for each
-            // profile, so it inherits from the parent user.
-            if (userHandle == USER_ALL || mCurrentManagedProfiles.contains(userHandle)) {
-                userHandle = mCurrentUserId;
-            }
-            if (mUsersUsersAllowingNotifications.indexOfKey(userHandle) < 0) {
-                // TODO(b/301955929): STOP_SHIP (stop flag flip): remove this read and use a safe
-                // default value before moving to 'released'
-                Log.wtf(TAG, "Asking for show notifs setting too early", new Throwable());
-                updateUserShowSettings(userHandle);
-            }
-            if (mUsersDpcAllowingNotifications.indexOfKey(userHandle) < 0) {
-                // TODO(b/301955929): STOP_SHIP (stop flag flip): remove this read and use a safe
-                // default value before moving to 'released'
-                Log.wtf(TAG, "Asking for show notifs dpm override too early", new Throwable());
-                updateDpcSettings(userHandle);
-            }
-            if (keyguardPrivateNotifications()) {
-                return mUsersUsersAllowingNotifications.get(userHandle)
-                        && mUsersDpcAllowingNotifications.get(userHandle);
-            } else {
-                return mUsersUsersAllowingNotifications.get(userHandle)
-                        && mUsersDpcAllowingNotifications.get(userHandle)
-                        && mKeyguardAllowingNotifications;
-            }
+        // Unlike 'show private', settings does not show a copy of this setting for each
+        // profile, so it inherits from the parent user.
+        if (userHandle == USER_ALL || mCurrentManagedProfiles.contains(userHandle)) {
+            userHandle = mCurrentUserId;
+        }
+        if (mUsersUsersAllowingNotifications.indexOfKey(userHandle) < 0) {
+            // TODO(b/301955929): STOP_SHIP (stop flag flip): remove this read and use a safe
+            // default value before moving to 'released'
+            Log.wtf(TAG, "Asking for show notifs setting too early", new Throwable());
+            updateUserShowSettings(userHandle);
+        }
+        if (mUsersDpcAllowingNotifications.indexOfKey(userHandle) < 0) {
+            // TODO(b/301955929): STOP_SHIP (stop flag flip): remove this read and use a safe
+            // default value before moving to 'released'
+            Log.wtf(TAG, "Asking for show notifs dpm override too early", new Throwable());
+            updateDpcSettings(userHandle);
+        }
+        if (keyguardPrivateNotifications()) {
+            return mUsersUsersAllowingNotifications.get(userHandle)
+                    && mUsersDpcAllowingNotifications.get(userHandle);
         } else {
-            if (isCurrentProfile(userHandle) && userHandle != mCurrentUserId) {
-                return true;
-            }
-
-            if (mUsersAllowingNotifications.indexOfKey(userHandle) < 0) {
-                final boolean allowedByUser = 0 != mSecureSettings.getIntForUser(
-                        LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, userHandle);
-                final boolean allowedByDpm = adminAllowsKeyguardFeature(userHandle,
-                        KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
-                final boolean allowedBySystem = mKeyguardManager.getPrivateNotificationsAllowed();
-                final boolean allowed = allowedByUser && allowedByDpm && allowedBySystem;
-                mUsersAllowingNotifications.append(userHandle, allowed);
-                return allowed;
-            }
-            return mUsersAllowingNotifications.get(userHandle);
+            return mUsersUsersAllowingNotifications.get(userHandle)
+                    && mUsersDpcAllowingNotifications.get(userHandle)
+                    && mKeyguardAllowingNotifications;
         }
     }
 
@@ -766,13 +681,7 @@
             return true;
         }
         NotificationEntry entry = mCommonNotifCollectionLazy.get().getEntry(key);
-        if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
-            return entry != null && entry.isChannelVisibilityPrivate();
-        } else {
-            return entry != null
-                    && entry.getRanking().getLockscreenVisibilityOverride()
-                    == Notification.VISIBILITY_PRIVATE;
-        }
+        return entry != null && entry.isChannelVisibilityPrivate();
     }
 
     @SuppressLint("MissingPermission")
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ColorUpdateLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ColorUpdateLogger.kt
index c8f996a..c416d43 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ColorUpdateLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ColorUpdateLogger.kt
@@ -60,7 +60,7 @@
         val didAppend = frames.lastOrNull()?.tryAddTrigger(event) == true
         if (!didAppend) {
             frames.add(Frame(event))
-            if (frames.size > maxFrames) frames.removeFirst()
+            if (frames.size > maxFrames) frames.removeAt(0)
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt
index 9fb453a..65ab4fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt
@@ -136,7 +136,7 @@
         }
 
         launch {
-            viewModel.clearAllButton.accessibilityDescriptionId.collect { textId ->
+            viewModel.manageOrHistoryButton.accessibilityDescriptionId.collect { textId ->
                 footer.setManageOrHistoryButtonDescription(textId)
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
index d7fe36f..332ece4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
@@ -219,15 +219,11 @@
     }
 
     private fun isRankingVisibilitySecret(entry: NotificationEntry): Boolean {
-        return if (featureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
-            // ranking.lockscreenVisibilityOverride contains possibly out of date DPC and Setting
-            // info, and NotificationLockscreenUserManagerImpl is already listening for updates
-            // to those
-            entry.ranking.channel != null && entry.ranking.channel.lockscreenVisibility ==
+        // ranking.lockscreenVisibilityOverride contains possibly out of date DPC and Setting
+        // info, and NotificationLockscreenUserManagerImpl is already listening for updates
+        // to those
+        return entry.ranking.channel != null && entry.ranking.channel.lockscreenVisibility ==
                     VISIBILITY_SECRET
-        } else {
-            entry.ranking.lockscreenVisibilityOverride == VISIBILITY_SECRET
-        }
     }
 
     override fun dump(pw: PrintWriter, args: Array<out String>) = pw.asIndenting().run {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java
index 3811f04..06410cd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java
@@ -431,28 +431,6 @@
 
     @Test
     public void publicMode_settingsDisallow() {
-        mFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, true);
-        // GIVEN an 'unfiltered-keyguard-showing' state
-        setupUnfilteredState(mEntry);
-
-        // WHEN the notification's user is in public mode and settings are configured to disallow
-        // notifications in public mode
-        when(mLockscreenUserManager.isLockscreenPublicMode(NOTIF_USER_ID)).thenReturn(true);
-        when(mLockscreenUserManager.userAllowsNotificationsInPublic(NOTIF_USER_ID))
-                .thenReturn(false);
-
-        mEntry.setRanking(new RankingBuilder()
-                .setChannel(new NotificationChannel("1", "1", 4))
-                .setVisibilityOverride(VISIBILITY_NO_OVERRIDE)
-                .setKey(mEntry.getKey()).build());
-
-        // THEN filter out the entry
-        assertTrue(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry));
-    }
-
-    @Test
-    public void publicMode_settingsDisallow_mainThread() {
-        mFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, false);
         // GIVEN an 'unfiltered-keyguard-showing' state
         setupUnfilteredState(mEntry);
 
@@ -473,7 +451,6 @@
 
     @Test
     public void publicMode_nullChannel_allowed() {
-        mFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, true);
         // GIVEN an 'unfiltered-keyguard-showing' state
         setupUnfilteredState(mEntry);
 
@@ -490,7 +467,6 @@
 
     @Test
     public void publicMode_notifDisallowed() {
-        mFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, true);
         NotificationChannel channel = new NotificationChannel("1", "1", IMPORTANCE_HIGH);
         channel.setLockscreenVisibility(VISIBILITY_SECRET);
         // GIVEN an 'unfiltered-keyguard-showing' state
@@ -509,23 +485,6 @@
     }
 
     @Test
-    public void publicMode_notifDisallowed_mainThread() {
-        mFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, false);
-        // GIVEN an 'unfiltered-keyguard-showing' state
-        setupUnfilteredState(mEntry);
-
-        // WHEN the notification's user is in public mode and settings are configured to disallow
-        // notifications in public mode
-        when(mLockscreenUserManager.isLockscreenPublicMode(CURR_USER_ID)).thenReturn(true);
-        mEntry.setRanking(new RankingBuilder()
-                .setKey(mEntry.getKey())
-                .setVisibilityOverride(VISIBILITY_SECRET).build());
-
-        // THEN filter out the entry
-        assertTrue(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry));
-    }
-
-    @Test
     public void doesNotExceedThresholdToShow() {
         // GIVEN an 'unfiltered-keyguard-showing' state
         setupUnfilteredState(mEntry);
@@ -579,7 +538,6 @@
 
     @Test
     public void notificationChannelVisibilityNoOverride() {
-        mFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, true);
         // GIVEN a VISIBILITY_PRIVATE notification
         NotificationEntryBuilder entryBuilder = new NotificationEntryBuilder()
                 .setUser(new UserHandle(NOTIF_USER_ID));
@@ -602,7 +560,6 @@
 
     @Test
     public void notificationChannelVisibilitySecret() {
-        mFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, true);
         // GIVEN a VISIBILITY_PRIVATE notification
         NotificationEntryBuilder entryBuilder = new NotificationEntryBuilder()
                 .setUser(new UserHandle(NOTIF_USER_ID));
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModelKosmos.kt
index 9fb3284..f1784a8 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModelKosmos.kt
@@ -18,6 +18,7 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
@@ -28,5 +29,6 @@
         keyguardTransitionInteractor = keyguardTransitionInteractor,
         goneToAodTransitionViewModel = goneToAodTransitionViewModel,
         goneToDozingTransitionViewModel = goneToDozingTransitionViewModel,
+        keyguardInteractor = keyguardInteractor,
     )
 }
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index d47245e..5a34217 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -2770,6 +2770,10 @@
                     + id + " destroyed");
             return;
         }
+        if (sDebug) {
+            Slog.d(TAG, "setAuthenticationResultLocked(): id= " + authenticationId
+                    + ", data=" + data);
+        }
         final int requestId = AutofillManager.getRequestIdFromAuthenticationId(authenticationId);
         if (requestId == AUGMENTED_AUTOFILL_REQUEST_ID) {
             setAuthenticationResultForAugmentedAutofillLocked(data, authenticationId);
@@ -2823,12 +2827,18 @@
                     + ", clientState=" + newClientState + ", authenticationId=" + authenticationId);
         }
         if (result instanceof FillResponse) {
+            if (sDebug) {
+                Slog.d(TAG, "setAuthenticationResultLocked(): received FillResponse from"
+                        + " authentication flow");
+            }
             logAuthenticationStatusLocked(requestId, MetricsEvent.AUTOFILL_AUTHENTICATED);
             mPresentationStatsEventLogger.maybeSetAuthenticationResult(
                 AUTHENTICATION_RESULT_SUCCESS);
             replaceResponseLocked(authenticatedResponse, (FillResponse) result, newClientState);
         } else if (result instanceof GetCredentialResponse) {
-            Slog.d(TAG, "Received GetCredentialResponse from authentication flow");
+            if (sDebug) {
+                Slog.d(TAG, "Received GetCredentialResponse from authentication flow");
+            }
             boolean isCredmanCallbackInvoked = false;
             if (Flags.autofillCredmanIntegration()) {
                 GetCredentialResponse response = (GetCredentialResponse) result;
@@ -2843,6 +2853,10 @@
                 }
             }
         } else if (result instanceof Dataset) {
+            if (sDebug) {
+                Slog.d(TAG, "setAuthenticationResultLocked(): received Dataset from"
+                        + " authentication flow");
+            }
             if (datasetIdx != AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED) {
                 logAuthenticationStatusLocked(requestId,
                         MetricsEvent.AUTOFILL_DATASET_AUTHENTICATED);
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java
index fab769e..40e9198 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java
@@ -75,6 +75,6 @@
     protected enum Type {
         THERMAL,
         POWER,
-        BEDTIME_MODE,
+        WEAR_BEDTIME_MODE,
     }
 }
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
index 2c02fc6..bc5fcb4 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
@@ -156,6 +156,8 @@
             return BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL;
         } else if (mClamperType == Type.POWER) {
             return BrightnessInfo.BRIGHTNESS_MAX_REASON_POWER_IC;
+        } else if (mClamperType == Type.WEAR_BEDTIME_MODE) {
+            return BrightnessInfo.BRIGHTNESS_MAX_REASON_WEAR_BEDTIME_MODE;
         } else {
             Slog.wtf(TAG, "BrightnessMaxReason not mapped for type=" + mClamperType);
             return BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamper.java
index 7e853bf..1902e35 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamper.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamper.java
@@ -64,7 +64,7 @@
     @NonNull
     @Override
     Type getType() {
-        return Type.BEDTIME_MODE;
+        return Type.WEAR_BEDTIME_MODE;
     }
 
     @Override
diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodClientInvoker.java b/services/core/java/com/android/server/inputmethod/IInputMethodClientInvoker.java
index 977dbff..84a59b4 100644
--- a/services/core/java/com/android/server/inputmethod/IInputMethodClientInvoker.java
+++ b/services/core/java/com/android/server/inputmethod/IInputMethodClientInvoker.java
@@ -117,6 +117,30 @@
     }
 
     @AnyThread
+    void onStartInputResult(@NonNull InputBindResult res, int startInputSeq) {
+        if (mIsProxy) {
+            onStartInputResultInternal(res, startInputSeq);
+        } else {
+            mHandler.post(() -> onStartInputResultInternal(res, startInputSeq));
+        }
+    }
+
+    @AnyThread
+    private void onStartInputResultInternal(@NonNull InputBindResult res, int startInputSeq) {
+        try {
+            mTarget.onStartInputResult(res, startInputSeq);
+        } catch (RemoteException e) {
+            logRemoteException(e);
+        } finally {
+            // Dispose the channel if the input method is not local to this process
+            // because the remote proxy will get its own copy when unparceled.
+            if (res.channel != null && mIsProxy) {
+                res.channel.dispose();
+            }
+        }
+    }
+
+    @AnyThread
     void onBindAccessibilityService(@NonNull InputBindResult res, int id) {
         if (mIsProxy) {
             onBindAccessibilityServiceInternal(res, id);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index bc169ca..5574d18 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -1539,7 +1539,13 @@
         @Override
         public void onStart() {
             mService.publishLocalService();
-            publishBinderService(Context.INPUT_METHOD_SERVICE, mService, false /*allowIsolated*/,
+            IInputMethodManager.Stub service;
+            if (Flags.useZeroJankProxy()) {
+                service = new ZeroJankProxy(mService.mHandler::post, mService);
+            } else {
+                service = mService;
+            }
+            publishBinderService(Context.INPUT_METHOD_SERVICE, service, false /*allowIsolated*/,
                     DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL | DUMP_FLAG_PROTO);
         }
 
@@ -2216,6 +2222,14 @@
         }
     }
 
+    @Nullable
+    ClientState getClientState(IInputMethodClient client) {
+        synchronized (ImfLock.class) {
+            return mClientController.getClient(client.asBinder());
+        }
+    }
+
+    // TODO(b/314150112): Move this to ClientController.
     @GuardedBy("ImfLock.class")
     void unbindCurrentClientLocked(@UnbindReason int unbindClientReason) {
         if (mCurClient != null) {
@@ -3741,6 +3755,20 @@
         return imeClientFocus == WindowManagerInternal.ImeClientFocusResult.HAS_IME_FOCUS;
     }
 
+    //TODO(b/293640003): merge with startInputOrWindowGainedFocus once Flags.useZeroJankProxy()
+    // is enabled.
+    @Override
+    public void startInputOrWindowGainedFocusAsync(
+            @StartInputReason int startInputReason, IInputMethodClient client, IBinder windowToken,
+            @StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode,
+            int windowFlags, @Nullable EditorInfo editorInfo,
+            IRemoteInputConnection inputConnection,
+            IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
+            int unverifiedTargetSdkVersion, @UserIdInt int userId,
+            @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq) {
+        // implemented by ZeroJankProxy
+    }
+
     @NonNull
     @Override
     public InputBindResult startInputOrWindowGainedFocus(
diff --git a/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java b/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
new file mode 100644
index 0000000..692fd7dc
--- /dev/null
+++ b/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
@@ -0,0 +1,406 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+/*
+ * Copyright (C) 2024 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.inputmethod;
+
+import static com.android.server.inputmethod.InputMethodManagerService.TAG;
+
+import android.Manifest;
+import android.annotation.BinderThread;
+import android.annotation.EnforcePermission;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.UserIdInt;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.util.ExceptionUtils;
+import android.util.Slog;
+import android.view.WindowManager;
+import android.view.inputmethod.CursorAnchorInfo;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.ImeTracker;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.InputMethodSubtype;
+import android.window.ImeOnBackInvokedDispatcher;
+
+import com.android.internal.inputmethod.DirectBootAwareness;
+import com.android.internal.inputmethod.IConnectionlessHandwritingCallback;
+import com.android.internal.inputmethod.IImeTracker;
+import com.android.internal.inputmethod.IInputMethodClient;
+import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
+import com.android.internal.inputmethod.IRemoteInputConnection;
+import com.android.internal.inputmethod.InputBindResult;
+import com.android.internal.inputmethod.SoftInputShowHideReason;
+import com.android.internal.inputmethod.StartInputFlags;
+import com.android.internal.inputmethod.StartInputReason;
+import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
+import com.android.internal.view.IInputMethodManager;
+
+import java.io.FileDescriptor;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+
+/**
+ * A proxy that processes all {@link IInputMethodManager} calls asynchronously.
+ * @hide
+ */
+public class ZeroJankProxy extends IInputMethodManager.Stub {
+
+    private final IInputMethodManager mInner;
+    private final Executor mExecutor;
+
+    ZeroJankProxy(Executor executor, IInputMethodManager inner) {
+        mInner = inner;
+        mExecutor = executor;
+    }
+
+    private void offload(ThrowingRunnable r) {
+        offloadInner(r);
+    }
+
+    private void offload(Runnable r) {
+        offloadInner(r);
+    }
+
+    private void offloadInner(Runnable r) {
+        boolean useThrowingRunnable = r instanceof ThrowingRunnable;
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            mExecutor.execute(() -> {
+                final long inner = Binder.clearCallingIdentity();
+                // Restoring calling identity, so we can still do permission checks on caller.
+                Binder.restoreCallingIdentity(identity);
+                try {
+                    try {
+                        if (useThrowingRunnable) {
+                            ((ThrowingRunnable) r).runOrThrow();
+                        } else {
+                            r.run();
+                        }
+                    } catch (Exception e) {
+                        Slog.e(TAG, "Error in async call", e);
+                        throw ExceptionUtils.propagate(e);
+                    }
+                } finally {
+                    Binder.restoreCallingIdentity(inner);
+                }
+            });
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
+    public void addClient(IInputMethodClient client, IRemoteInputConnection inputConnection,
+            int selfReportedDisplayId) throws RemoteException {
+        offload(() -> mInner.addClient(client, inputConnection, selfReportedDisplayId));
+    }
+
+    @Override
+    public InputMethodInfo getCurrentInputMethodInfoAsUser(int userId) throws RemoteException {
+        return mInner.getCurrentInputMethodInfoAsUser(userId);
+    }
+
+    @Override
+    public List<InputMethodInfo> getInputMethodList(
+            int userId, @DirectBootAwareness int directBootAwareness) throws RemoteException {
+        return mInner.getInputMethodList(userId, directBootAwareness);
+    }
+
+    @Override
+    public List<InputMethodInfo> getEnabledInputMethodList(int userId) throws RemoteException {
+        return mInner.getEnabledInputMethodList(userId);
+    }
+
+    @Override
+    public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(String imiId,
+            boolean allowsImplicitlyEnabledSubtypes, int userId)
+            throws RemoteException {
+        return mInner.getEnabledInputMethodSubtypeList(imiId, allowsImplicitlyEnabledSubtypes,
+                userId);
+    }
+
+    @Override
+    public InputMethodSubtype getLastInputMethodSubtype(int userId) throws RemoteException {
+        return mInner.getLastInputMethodSubtype(userId);
+    }
+
+    @Override
+    public boolean showSoftInput(IInputMethodClient client, IBinder windowToken,
+            @Nullable ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
+            int lastClickTooType, ResultReceiver resultReceiver,
+            @SoftInputShowHideReason int reason)
+            throws RemoteException {
+        offload(() -> mInner.showSoftInput(client, windowToken, statsToken, flags, lastClickTooType,
+                resultReceiver, reason));
+        return true;
+    }
+
+    @Override
+    public boolean hideSoftInput(IInputMethodClient client, IBinder windowToken,
+            @Nullable ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
+            ResultReceiver resultReceiver, @SoftInputShowHideReason int reason)
+            throws RemoteException {
+        offload(() -> mInner.hideSoftInput(client, windowToken, statsToken, flags, resultReceiver,
+                reason));
+        return true;
+    }
+
+    @Override
+    public void startInputOrWindowGainedFocusAsync(
+            @StartInputReason int startInputReason,
+            IInputMethodClient client, IBinder windowToken,
+            @StartInputFlags int startInputFlags,
+            @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode,
+            int windowFlags, @Nullable EditorInfo editorInfo,
+            IRemoteInputConnection inputConnection,
+            IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
+            int unverifiedTargetSdkVersion, @UserIdInt int userId,
+            @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq)
+            throws RemoteException {
+        offload(() -> {
+            InputBindResult result = mInner.startInputOrWindowGainedFocus(startInputReason, client,
+                    windowToken, startInputFlags, softInputMode, windowFlags,
+                    editorInfo,
+                    inputConnection, remoteAccessibilityInputConnection,
+                    unverifiedTargetSdkVersion,
+                    userId, imeDispatcher);
+            sendOnStartInputResult(client, result, startInputSeq);
+        });
+    }
+
+    @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+    @Override
+    public InputBindResult startInputOrWindowGainedFocus(
+            @StartInputReason int startInputReason,
+            IInputMethodClient client, IBinder windowToken,
+            @StartInputFlags int startInputFlags,
+            @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode,
+            int windowFlags, @Nullable EditorInfo editorInfo,
+            IRemoteInputConnection inputConnection,
+            IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
+            int unverifiedTargetSdkVersion, @UserIdInt int userId,
+            @NonNull ImeOnBackInvokedDispatcher imeDispatcher)
+            throws RemoteException {
+        // Should never be called when flag is enabled i.e. when this proxy is used.
+        return null;
+    }
+
+    @Override
+    public void showInputMethodPickerFromClient(IInputMethodClient client,
+            int auxiliarySubtypeMode)
+            throws RemoteException {
+        offload(() -> mInner.showInputMethodPickerFromClient(client, auxiliarySubtypeMode));
+    }
+
+    @EnforcePermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+    @Override
+    public void showInputMethodPickerFromSystem(int auxiliarySubtypeMode, int displayId)
+            throws RemoteException {
+        mInner.showInputMethodPickerFromSystem(auxiliarySubtypeMode, displayId);
+    }
+
+    @EnforcePermission(Manifest.permission.TEST_INPUT_METHOD)
+    @Override
+    public boolean isInputMethodPickerShownForTest() throws RemoteException {
+        super.isInputMethodPickerShownForTest_enforcePermission();
+        return mInner.isInputMethodPickerShownForTest();
+    }
+
+    @Override
+    public InputMethodSubtype getCurrentInputMethodSubtype(int userId) throws RemoteException {
+        return mInner.getCurrentInputMethodSubtype(userId);
+    }
+
+    @Override
+    public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes,
+            @UserIdInt int userId) throws RemoteException {
+        mInner.setAdditionalInputMethodSubtypes(imiId, subtypes, userId);
+    }
+
+    @Override
+    public void setExplicitlyEnabledInputMethodSubtypes(String imeId,
+            @NonNull int[] subtypeHashCodes, @UserIdInt int userId) throws RemoteException {
+        mInner.setExplicitlyEnabledInputMethodSubtypes(imeId, subtypeHashCodes, userId);
+    }
+
+    @Override
+    public int getInputMethodWindowVisibleHeight(IInputMethodClient client)
+            throws RemoteException {
+        return mInner.getInputMethodWindowVisibleHeight(client);
+    }
+
+    @Override
+    public void reportPerceptibleAsync(IBinder windowToken, boolean perceptible)
+            throws RemoteException {
+        // Already async TODO(b/293640003): ordering issues?
+        mInner.reportPerceptibleAsync(windowToken, perceptible);
+    }
+
+    @EnforcePermission(Manifest.permission.INTERNAL_SYSTEM_WINDOW)
+    @Override
+    public void removeImeSurface() throws RemoteException {
+        mInner.removeImeSurface();
+    }
+
+    @Override
+    public void removeImeSurfaceFromWindowAsync(IBinder windowToken) throws RemoteException {
+        mInner.removeImeSurfaceFromWindowAsync(windowToken);
+    }
+
+    @Override
+    public void startProtoDump(byte[] bytes, int i, String s) throws RemoteException {
+        mInner.startProtoDump(bytes, i, s);
+    }
+
+    @Override
+    public boolean isImeTraceEnabled() throws RemoteException {
+        return mInner.isImeTraceEnabled();
+    }
+
+    @EnforcePermission(Manifest.permission.CONTROL_UI_TRACING)
+    @Override
+    public void startImeTrace() throws RemoteException {
+        mInner.startImeTrace();
+    }
+
+    @EnforcePermission(Manifest.permission.CONTROL_UI_TRACING)
+    @Override
+    public void stopImeTrace() throws RemoteException {
+        mInner.stopImeTrace();
+    }
+
+    @Override
+    public void startStylusHandwriting(IInputMethodClient client)
+            throws RemoteException {
+        offload(() -> mInner.startStylusHandwriting(client));
+    }
+
+    @Override
+    public void startConnectionlessStylusHandwriting(IInputMethodClient client, int userId,
+            @Nullable CursorAnchorInfo cursorAnchorInfo, @Nullable String delegatePackageName,
+            @Nullable String delegatorPackageName,
+            @NonNull IConnectionlessHandwritingCallback callback) throws RemoteException {
+        offload(() -> mInner.startConnectionlessStylusHandwriting(
+                client, userId, cursorAnchorInfo, delegatePackageName, delegatorPackageName,
+                callback));
+    }
+
+    @Override
+    public boolean acceptStylusHandwritingDelegation(
+            @NonNull IInputMethodClient client,
+            @UserIdInt int userId,
+            @NonNull String delegatePackageName,
+            @NonNull String delegatorPackageName,
+            @InputMethodManager.HandwritingDelegateFlags int flags) {
+        try {
+            return CompletableFuture.supplyAsync(() -> {
+                try {
+                    return mInner.acceptStylusHandwritingDelegation(
+                            client, userId, delegatePackageName, delegatorPackageName, flags);
+                } catch (RemoteException e) {
+                    throw new RuntimeException(e);
+                }
+            }, this::offload).get();
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        } catch (ExecutionException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public void prepareStylusHandwritingDelegation(
+            @NonNull IInputMethodClient client,
+            @UserIdInt int userId,
+            @NonNull String delegatePackageName,
+            @NonNull String delegatorPackageName) {
+        offload(() -> mInner.prepareStylusHandwritingDelegation(
+                client, userId, delegatePackageName, delegatorPackageName));
+    }
+
+    @Override
+    public boolean isStylusHandwritingAvailableAsUser(int userId, boolean connectionless)
+            throws RemoteException {
+        return mInner.isStylusHandwritingAvailableAsUser(userId, connectionless);
+    }
+
+    @EnforcePermission("android.permission.TEST_INPUT_METHOD")
+    @Override
+    public void addVirtualStylusIdForTestSession(IInputMethodClient client)
+            throws RemoteException {
+        mInner.addVirtualStylusIdForTestSession(client);
+    }
+
+    @EnforcePermission("android.permission.TEST_INPUT_METHOD")
+    @Override
+    public void setStylusWindowIdleTimeoutForTest(IInputMethodClient client, long timeout)
+            throws RemoteException {
+        mInner.setStylusWindowIdleTimeoutForTest(client, timeout);
+    }
+
+    @Override
+    public IImeTracker getImeTrackerService() throws RemoteException {
+        return mInner.getImeTrackerService();
+    }
+
+    @BinderThread
+    @Override
+    public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
+            @Nullable FileDescriptor err,
+            @NonNull String[] args, @Nullable ShellCallback callback,
+            @NonNull ResultReceiver resultReceiver) throws RemoteException {
+        ((InputMethodManagerService) mInner).onShellCommand(
+                in, out, err, args, callback, resultReceiver);
+    }
+
+    private void sendOnStartInputResult(
+            IInputMethodClient client, InputBindResult res, int startInputSeq) {
+        InputMethodManagerService service = (InputMethodManagerService) mInner;
+        final ClientState cs = service.getClientState(client);
+        if (cs != null && cs.mClient != null) {
+            cs.mClient.onStartInputResult(res, startInputSeq);
+        } else {
+            // client is unbound.
+            Slog.i(TAG, "Client that requested startInputOrWindowGainedFocus is no longer"
+                    + " bound. InputBindResult: " + res + " for startInputSeq: " + startInputSeq);
+        }
+    }
+}
+
diff --git a/services/core/java/com/android/server/wearable/WearableSensingManagerService.java b/services/core/java/com/android/server/wearable/WearableSensingManagerService.java
index 00c3026..d05482d 100644
--- a/services/core/java/com/android/server/wearable/WearableSensingManagerService.java
+++ b/services/core/java/com/android/server/wearable/WearableSensingManagerService.java
@@ -55,6 +55,7 @@
 import com.android.server.utils.quota.MultiRateLimiter;
 
 import java.io.FileDescriptor;
+import java.time.Duration;
 import java.util.HashSet;
 import java.util.Objects;
 import java.util.Set;
@@ -106,7 +107,7 @@
     private final Context mContext;
     private final AtomicInteger mNextDataRequestObserverId = new AtomicInteger(1);
     private final Set<DataRequestObserverContext> mDataRequestObserverContexts = new HashSet<>();
-    private final MultiRateLimiter mDataRequestRateLimiter;
+    @NonNull private volatile MultiRateLimiter mDataRequestRateLimiter;
     volatile boolean mIsServiceEnabled;
 
     public WearableSensingManagerService(Context context) {
@@ -238,6 +239,57 @@
         }
     }
 
+    /**
+     * Sets the window size used in data request rate limiting.
+     *
+     * <p>The new value will not be reflected in {@link
+     * WearableSensingDataRequest#getRateLimitWindowSize()}.
+     *
+     * <p>{@code windowSize} will be automatically capped between
+     * com.android.server.utils.quota.QuotaTracker#MIN_WINDOW_SIZE_MS and
+     * com.android.server.utils.quota.QuotaTracker#MAX_WINDOW_SIZE_MS
+     *
+     * <p>The current rate limit will also be reset.
+     *
+     * <p>This method is only used for testing and must not be called in production code because
+     * it effectively bypasses the rate limiting introduced to enhance privacy protection.
+     */
+    @VisibleForTesting
+    void setDataRequestRateLimitWindowSize(@NonNull Duration windowSize) {
+        Slog.w(
+                TAG,
+                TextUtils.formatSimple(
+                        "Setting the data request rate limit window size to %s. This also resets"
+                            + " the current limit and should only be callable from a test.",
+                        windowSize));
+        mDataRequestRateLimiter =
+                new MultiRateLimiter.Builder(mContext)
+                        .addRateLimit(WearableSensingDataRequest.getRateLimit(), windowSize)
+                        .build();
+    }
+
+    /**
+     * Resets the window size used in data request rate limiting back to the default value.
+     *
+     * <p>The current rate limit will also be reset.
+     *
+     * <p>This method is only used for testing and must not be called in production code because
+     * it effectively bypasses the rate limiting introduced to enhance privacy protection.
+     */
+    @VisibleForTesting
+    void resetDataRequestRateLimitWindowSize() {
+        Slog.w(
+                TAG,
+                "Resetting the data request rate limit window size back to the default value. This"
+                    + " also resets the current limit and should only be callable from a test.");
+        mDataRequestRateLimiter =
+                new MultiRateLimiter.Builder(mContext)
+                        .addRateLimit(
+                                WearableSensingDataRequest.getRateLimit(),
+                                WearableSensingDataRequest.getRateLimitWindowSize())
+                        .build();
+    }
+
     private DataRequestObserverContext getDataRequestObserverContext(
             int dataType, int userId, PendingIntent dataRequestPendingIntent) {
         synchronized (mDataRequestObserverContexts) {
diff --git a/services/core/java/com/android/server/wearable/WearableSensingShellCommand.java b/services/core/java/com/android/server/wearable/WearableSensingShellCommand.java
index 842bccb..0a9cf34 100644
--- a/services/core/java/com/android/server/wearable/WearableSensingShellCommand.java
+++ b/services/core/java/com/android/server/wearable/WearableSensingShellCommand.java
@@ -29,6 +29,7 @@
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.PrintWriter;
+import java.time.Duration;
 
 final class WearableSensingShellCommand extends ShellCommand {
     private static final String TAG = WearableSensingShellCommand.class.getSimpleName();
@@ -90,6 +91,8 @@
                 return getBoundPackageName();
             case "set-temporary-service":
                 return setTemporaryService();
+            case "set-data-request-rate-limit-window-size":
+                return setDataRequestRateLimitWindowSize();
             default:
                 return handleDefaultCommands(cmd);
         }
@@ -114,6 +117,11 @@
         pw.println("  set-temporary-service USER_ID [PACKAGE_NAME] [COMPONENT_NAME DURATION]");
         pw.println("    Temporarily (for DURATION ms) changes the service implementation.");
         pw.println("    To reset, call with just the USER_ID argument.");
+        pw.println("  set-data-request-rate-limit-window-size WINDOW_SIZE");
+        pw.println("    Set the window size used in data request rate limiting to WINDOW_SIZE"
+                + " seconds.");
+        pw.println("    positive WINDOW_SIZE smaller than 20 will be automatically set to 20.");
+        pw.println("    To reset, call with 0 or a negative WINDOW_SIZE.");
     }
 
     private int createDataStream() {
@@ -209,4 +217,20 @@
         resultPrinter.println(componentName == null ? "" : componentName.getPackageName());
         return 0;
     }
+
+    private int setDataRequestRateLimitWindowSize() {
+        Slog.d(TAG, "setDataRequestRateLimitWindowSize");
+        int windowSizeSeconds = Integer.parseInt(getNextArgRequired());
+        if (windowSizeSeconds <= 0) {
+            mService.resetDataRequestRateLimitWindowSize();
+        } else {
+            // 20 is the minimum window size supported by the rate limiter.
+            // It is defined by com.android.server.utils.quota.QuotaTracker#MIN_WINDOW_SIZE_MS
+            if (windowSizeSeconds < 20) {
+                windowSizeSeconds = 20;
+            }
+            mService.setDataRequestRateLimitWindowSize(Duration.ofSeconds(windowSizeSeconds));
+        }
+        return 0;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index 2c49203..533529a 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -639,6 +639,10 @@
         return mPendingRemoteAnimationRegistry;
     }
 
+    ActivityRecord getLastStartActivity() {
+        return mLastStarter != null ? mLastStarter.mStartActivity : null;
+    }
+
     void dumpLastHomeActivityStartResult(PrintWriter pw, String prefix) {
         pw.print(prefix);
         pw.print("mLastHomeActivityStartResult=");
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 3637ab1..adbe800 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -5554,7 +5554,7 @@
      * Saves the current activity manager state and includes the saved state in the next dump of
      * activity manager.
      */
-    void saveANRState(String reason) {
+    void saveANRState(ActivityRecord activity, String reason) {
         final StringWriter sw = new StringWriter();
         final PrintWriter pw = new FastPrintWriter(sw, false, 1024);
         pw.println("  ANR time: " + DateFormat.getDateTimeInstance().format(new Date()));
@@ -5562,14 +5562,25 @@
             pw.println("  Reason: " + reason);
         }
         pw.println();
-        getActivityStartController().dump(pw, "  ", null);
-        pw.println();
+        if (activity != null) {
+            final Task rootTask = activity.getRootTask();
+            if (rootTask != null) {
+                rootTask.forAllTaskFragments(
+                        tf -> tf.dumpInner("  ", pw, true /* dumpAll */, null /* dumpPackage */));
+                pw.println();
+            }
+            mActivityStartController.dump(pw, "  ", activity.packageName);
+            if (mActivityStartController.getLastStartActivity() != activity) {
+                activity.dump(pw, "  ", true /* dumpAll */);
+            }
+        }
+        ActivityTaskSupervisor.printThisActivity(pw, mRootWindowContainer.getTopResumedActivity(),
+                null /* dumpPackage */, INVALID_DISPLAY, true /* needSep */,
+                "  ResumedActivity: ", /* header= */ null /* header */);
+        mLockTaskController.dump(pw, "  ");
+        mKeyguardController.dump(pw, "  ");
         pw.println("-------------------------------------------------------------------"
                 + "------------");
-        dumpActivitiesLocked(null /* fd */, pw, null /* args */, 0 /* opti */,
-                true /* dumpAll */, false /* dumpClient */, null /* dumpPackage */,
-                INVALID_DISPLAY, "" /* header */);
-        pw.println();
         pw.close();
 
         mLastANRState = sw.toString();
diff --git a/services/core/java/com/android/server/wm/AnrController.java b/services/core/java/com/android/server/wm/AnrController.java
index b9f6e17..0013c5c 100644
--- a/services/core/java/com/android/server/wm/AnrController.java
+++ b/services/core/java/com/android/server/wm/AnrController.java
@@ -367,7 +367,7 @@
                 Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "dumpAnrStateLocked()");
                 synchronized (mService.mGlobalLock) {
                     mService.saveANRStateLocked(activity, windowState, reason);
-                    mService.mAtmService.saveANRState(reason);
+                    mService.mAtmService.saveANRState(activity, reason);
                 }
             } finally {
                 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
diff --git a/services/core/java/com/android/server/wm/ClientLifecycleManager.java b/services/core/java/com/android/server/wm/ClientLifecycleManager.java
index 5b4fb3e..e48e4e8 100644
--- a/services/core/java/com/android/server/wm/ClientLifecycleManager.java
+++ b/services/core/java/com/android/server/wm/ClientLifecycleManager.java
@@ -87,11 +87,7 @@
     void scheduleTransactionItemNow(@NonNull IApplicationThread client,
             @NonNull ClientTransactionItem transactionItem) throws RemoteException {
         final ClientTransaction clientTransaction = ClientTransaction.obtain(client);
-        if (transactionItem.isActivityLifecycleItem()) {
-            clientTransaction.setLifecycleStateRequest((ActivityLifecycleItem) transactionItem);
-        } else {
-            clientTransaction.addCallback(transactionItem);
-        }
+        clientTransaction.addTransactionItem(transactionItem);
         scheduleTransaction(clientTransaction);
     }
 
@@ -115,11 +111,8 @@
         } else {
             // TODO(b/260873529): cleanup after launch.
             final ClientTransaction clientTransaction = ClientTransaction.obtain(client);
-            if (transactionItem.isActivityLifecycleItem()) {
-                clientTransaction.setLifecycleStateRequest((ActivityLifecycleItem) transactionItem);
-            } else {
-                clientTransaction.addCallback(transactionItem);
-            }
+            clientTransaction.addTransactionItem(transactionItem);
+
             scheduleTransaction(clientTransaction);
         }
     }
@@ -160,8 +153,8 @@
         } else {
             // TODO(b/260873529): cleanup after launch.
             final ClientTransaction clientTransaction = ClientTransaction.obtain(client);
-            clientTransaction.addCallback(transactionItem);
-            clientTransaction.setLifecycleStateRequest(lifecycleItem);
+            clientTransaction.addTransactionItem(transactionItem);
+            clientTransaction.addTransactionItem(lifecycleItem);
             scheduleTransaction(clientTransaction);
         }
     }
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 9601964..5cf9acd 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -104,7 +104,6 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.util.ArraySet;
-import android.util.PrintWriterPrinter;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.DisplayInfo;
@@ -2880,9 +2879,6 @@
         if (!CLIENT_TRANSIENT) {
             mSystemGestures.dump(pw, prefix);
         }
-
-        pw.print(prefix); pw.println("Looper state:");
-        mHandler.getLooper().dump(new PrintWriterPrinter(pw), prefix + "  ");
     }
 
     private boolean supportsPointerLocation() {
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 975208f..908cbd3 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -110,9 +110,7 @@
     private final String mStringName;
     SurfaceSession mSurfaceSession;
     private final ArrayList<WindowState> mAddedWindows = new ArrayList<>();
-    // Set of visible application overlay window surfaces connected to this session.
-    private final ArraySet<WindowSurfaceController> mAppOverlaySurfaces = new ArraySet<>();
-    // Set of visible alert window surfaces connected to this session.
+    /** Set of visible alert/app-overlay window surfaces connected to this session. */
     private final ArraySet<WindowSurfaceController> mAlertWindowSurfaces = new ArraySet<>();
     private final DragDropController mDragDropController;
     final boolean mCanAddInternalSystemWindow;
@@ -796,46 +794,45 @@
         }
 
         boolean changed;
-
-        if (!mCanAddInternalSystemWindow && !mCanCreateSystemApplicationOverlay) {
-            // We want to track non-system apps adding alert windows so we can post an
-            // on-going notification for the user to control their visibility.
-            if (visible) {
-                changed = mAlertWindowSurfaces.add(surfaceController);
-                MetricsLoggerWrapper.logAppOverlayEnter(mUid, mPackageName, changed, type, true);
-            } else {
-                changed = mAlertWindowSurfaces.remove(surfaceController);
-                MetricsLoggerWrapper.logAppOverlayExit(mUid, mPackageName, changed, type, true);
+        // Track non-system apps adding overlay/alert windows, so a notification can post for the
+        // user to control their visibility.
+        final boolean noSystemOverlayPermission =
+                !mCanAddInternalSystemWindow && !mCanCreateSystemApplicationOverlay;
+        if (visible) {
+            changed = mAlertWindowSurfaces.add(surfaceController);
+            if (type == TYPE_APPLICATION_OVERLAY) {
+                MetricsLoggerWrapper.logAppOverlayEnter(mUid, mPackageName, changed, type,
+                        false /* set false to only log for TYPE_APPLICATION_OVERLAY */);
+            } else if (noSystemOverlayPermission) {
+                MetricsLoggerWrapper.logAppOverlayEnter(mUid, mPackageName, changed, type,
+                        true /* only log for non-TYPE_APPLICATION_OVERLAY */);
             }
+        } else {
+            changed = mAlertWindowSurfaces.remove(surfaceController);
+            if (type == TYPE_APPLICATION_OVERLAY) {
+                MetricsLoggerWrapper.logAppOverlayExit(mUid, mPackageName, changed, type,
+                        false /* set false to only log for TYPE_APPLICATION_OVERLAY */);
+            } else if (noSystemOverlayPermission) {
+                MetricsLoggerWrapper.logAppOverlayExit(mUid, mPackageName, changed, type,
+                        true /* only log for non-TYPE_APPLICATION_OVERLAY */);
+            }
+        }
 
-            if (changed) {
-                if (mAlertWindowSurfaces.isEmpty()) {
-                    cancelAlertWindowNotification();
-                } else if (mAlertWindowNotification == null){
-                    mAlertWindowNotification = new AlertWindowNotification(mService, mPackageName);
-                    if (mShowingAlertWindowNotificationAllowed) {
-                        mAlertWindowNotification.post();
-                    }
+        if (changed && noSystemOverlayPermission) {
+            if (mAlertWindowSurfaces.isEmpty()) {
+                cancelAlertWindowNotification();
+            } else if (mAlertWindowNotification == null) {
+                mAlertWindowNotification = new AlertWindowNotification(mService, mPackageName);
+                if (mShowingAlertWindowNotificationAllowed) {
+                    mAlertWindowNotification.post();
                 }
             }
         }
 
-        if (type != TYPE_APPLICATION_OVERLAY) {
-            return;
-        }
-
-        if (visible) {
-            changed = mAppOverlaySurfaces.add(surfaceController);
-            MetricsLoggerWrapper.logAppOverlayEnter(mUid, mPackageName, changed, type, false);
-        } else {
-            changed = mAppOverlaySurfaces.remove(surfaceController);
-            MetricsLoggerWrapper.logAppOverlayExit(mUid, mPackageName, changed, type, false);
-        }
-
-        if (changed) {
-            // Notify activity manager of changes to app overlay windows so it can adjust the
-            // importance score for the process.
-            setHasOverlayUi(!mAppOverlaySurfaces.isEmpty());
+        if (changed && mPid != WindowManagerService.MY_PID) {
+            // Notify activity manager that the process contains overlay/alert windows, so it can
+            // adjust the importance score for the process.
+            setHasOverlayUi(!mAlertWindowSurfaces.isEmpty());
         }
     }
 
@@ -870,12 +867,12 @@
         mSurfaceSession = null;
         mAddedWindows.clear();
         mAlertWindowSurfaces.clear();
-        mAppOverlaySurfaces.clear();
         setHasOverlayUi(false);
         cancelAlertWindowNotification();
     }
 
-    private void setHasOverlayUi(boolean hasOverlayUi) {
+    @VisibleForTesting
+    void setHasOverlayUi(boolean hasOverlayUi) {
         mService.mH.obtainMessage(H.SET_HAS_OVERLAY_UI, mPid, hasOverlayUi ? 1 : 0).sendToTarget();
     }
 
@@ -890,7 +887,6 @@
     void dump(PrintWriter pw, String prefix) {
         pw.print(prefix); pw.print("numWindow="); pw.print(mAddedWindows.size());
                 pw.print(" mCanAddInternalSystemWindow="); pw.print(mCanAddInternalSystemWindow);
-                pw.print(" mAppOverlaySurfaces="); pw.print(mAppOverlaySurfaces);
                 pw.print(" mAlertWindowSurfaces="); pw.print(mAlertWindowSurfaces);
                 pw.print(" mClientDead="); pw.print(mClientDead);
                 pw.print(" mSurfaceSession="); pw.println(mSurfaceSession);
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 838ce86..10cbc66 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -1590,7 +1590,7 @@
                             mAtmService.getLifecycleManager().scheduleTransactionItem(
                                     appThread, activityResultItem);
                         } else {
-                            transaction.addCallback(activityResultItem);
+                            transaction.addTransactionItem(activityResultItem);
                         }
                     }
                 }
@@ -1602,7 +1602,7 @@
                         mAtmService.getLifecycleManager().scheduleTransactionItem(
                                 appThread, newIntentItem);
                     } else {
-                        transaction.addCallback(newIntentItem);
+                        transaction.addTransactionItem(newIntentItem);
                     }
                 }
 
@@ -1624,7 +1624,7 @@
                     mAtmService.getLifecycleManager().scheduleTransactionItem(
                             appThread, resumeActivityItem);
                 } else {
-                    transaction.setLifecycleStateRequest(resumeActivityItem);
+                    transaction.addTransactionItem(resumeActivityItem);
                     mAtmService.getLifecycleManager().scheduleTransaction(transaction);
                 }
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index e538f5d5..61480d2 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -6750,11 +6750,6 @@
     private void dumpWindowsLocked(PrintWriter pw, boolean dumpAll,
             ArrayList<WindowState> windows) {
         pw.println("WINDOW MANAGER WINDOWS (dumpsys window windows)");
-        dumpWindowsNoHeaderLocked(pw, dumpAll, windows);
-    }
-
-    private void dumpWindowsNoHeaderLocked(PrintWriter pw, boolean dumpAll,
-            ArrayList<WindowState> windows) {
         mRoot.dumpWindowsNoHeader(pw, dumpAll, windows);
 
         if (!mHidingNonSystemOverlayWindows.isEmpty()) {
@@ -6989,9 +6984,15 @@
         if (reason != null) {
             pw.println("  Reason: " + reason);
         }
+        pw.println();
+        final ArrayList<WindowState> relatedWindows = new ArrayList<>();
         for (int i = mRoot.getChildCount() - 1; i >= 0; i--) {
             final DisplayContent dc = mRoot.getChildAt(i);
             final int displayId = dc.getDisplayId();
+            final WindowState currentFocus = dc.mCurrentFocus;
+            final ActivityRecord focusedApp = dc.mFocusedApp;
+            pw.println("  Display #" + displayId + " currentFocus=" + currentFocus
+                    + " focusedApp=" + focusedApp);
             if (!dc.mWinAddedSinceNullFocus.isEmpty()) {
                 pw.println("  Windows added in display #" + displayId + " since null focus: "
                         + dc.mWinAddedSinceNullFocus);
@@ -7000,12 +7001,25 @@
                 pw.println("  Windows removed in display #" + displayId + " since null focus: "
                         + dc.mWinRemovedSinceNullFocus);
             }
+            pw.println("  Tasks in top down Z order:");
+            dc.forAllTaskDisplayAreas(tda -> {
+                tda.dump(pw, "    ", false /* dumpAll */);
+            });
+            dc.getInputMonitor().dump(pw, "  ");
+            pw.println();
+            dc.forAllWindows(w -> {
+                if ((currentFocus != null && Objects.equals(w.mAttrs.packageName,
+                        currentFocus.mAttrs.packageName)) || (focusedApp != null
+                        && Objects.equals(w.mAttrs.packageName, focusedApp.packageName))) {
+                    relatedWindows.add(w);
+                }
+            }, true /* traverseTopToBottom */);
         }
+        if (windowState != null && !relatedWindows.contains(windowState)) {
+            relatedWindows.add(windowState);
+        }
+        mRoot.dumpWindowsNoHeader(pw, true /* dumpAll */, relatedWindows);
         pw.println();
-        dumpWindowsNoHeaderLocked(pw, true, null);
-        pw.println();
-        pw.println("Last ANR continued");
-        mRoot.dumpDisplayContents(pw);
         pw.close();
         mLastANRState = sw.toString();
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index cfd90f6..d5d26be 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -264,6 +264,7 @@
 
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_ENTRY_POINT_ADB;
 import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
 import static com.android.server.SystemTimeZone.TIME_ZONE_CONFIDENCE_HIGH;
 import static com.android.server.am.ActivityManagerService.STOCK_PM_FLAGS;
@@ -22099,9 +22100,9 @@
         enforcePermission(MANAGE_DEVICE_POLICY_THEFT_DETECTION, caller.getPackageName(),
                 caller.getUserId());
 
-        //STOPSHIP: replace 1<<9 with
-        // LockPatternUtils.SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST once ag/26042068 lands
-        return 0 != (mLockPatternUtils.getStrongAuthForUser(caller.getUserId()) & (1 << 9));
+        return mInjector.binderWithCleanCallingIdentity(() ->
+                0 != (mLockPatternUtils.getStrongAuthForUser(caller.getUserId())
+                        & SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST));
     }
 
     @Override
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamperTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamperTest.java
index 3458b08..306b4f8 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamperTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamperTest.java
@@ -85,7 +85,7 @@
 
     @Test
     public void testType() {
-        assertEquals(BrightnessClamper.Type.BEDTIME_MODE, mClamper.getType());
+        assertEquals(BrightnessClamper.Type.WEAR_BEDTIME_MODE, mClamper.getType());
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index 4307ec5..cea10ea 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -87,6 +87,7 @@
 import android.os.IRemoteCallback;
 import android.os.Looper;
 import android.os.Message;
+import android.os.PowerManagerInternal;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -1223,6 +1224,7 @@
         private final UserManagerInternal mUserManagerInternalMock;
         private final WindowManagerService mWindowManagerMock;
         private final ActivityTaskManagerInternal mActivityTaskManagerInternal;
+        private final PowerManagerInternal mPowerManagerInternal;
         private final KeyguardManager mKeyguardManagerMock;
         private final LockPatternUtils mLockPatternUtilsMock;
 
@@ -1244,6 +1246,7 @@
             mWindowManagerMock = mock(WindowManagerService.class);
             mActivityTaskManagerInternal = mock(ActivityTaskManagerInternal.class);
             mStorageManagerMock = mock(IStorageManager.class);
+            mPowerManagerInternal = mock(PowerManagerInternal.class);
             mKeyguardManagerMock = mock(KeyguardManager.class);
             when(mKeyguardManagerMock.isDeviceSecure(anyInt())).thenReturn(true);
             mLockPatternUtilsMock = mock(LockPatternUtils.class);
@@ -1309,6 +1312,11 @@
         }
 
         @Override
+        PowerManagerInternal getPowerManagerInternal() {
+            return mPowerManagerInternal;
+        }
+
+        @Override
         KeyguardManager getKeyguardManager() {
             return mKeyguardManagerMock;
         }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index 4da519c..c972e51 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -240,6 +240,22 @@
     }
 
     @Test
+    public void testTrackOverlayWindow() {
+        final WindowProcessController wpc = mSystemServicesTestRule.addProcess(
+                "pkgName", "processName", 1000 /* pid */, Process.SYSTEM_UID);
+        final Session session = createTestSession(mAtm, wpc.getPid(), wpc.mUid);
+        spyOn(session);
+        assertTrue(session.mCanAddInternalSystemWindow);
+        final WindowSurfaceController winSurface = mock(WindowSurfaceController.class);
+        session.onWindowSurfaceVisibilityChanged(winSurface, true /* visible */,
+                LayoutParams.TYPE_PHONE);
+        verify(session).setHasOverlayUi(true);
+        session.onWindowSurfaceVisibilityChanged(winSurface, false /* visible */,
+                LayoutParams.TYPE_PHONE);
+        verify(session).setHasOverlayUi(false);
+    }
+
+    @Test
     public void testRelayoutExitingWindow() {
         final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, "appWin");
         final WindowSurfaceController surfaceController = mock(WindowSurfaceController.class);
diff --git a/tests/BootImageProfileTest/AndroidTest.xml b/tests/BootImageProfileTest/AndroidTest.xml
index 7e97fa3..9b527dc 100644
--- a/tests/BootImageProfileTest/AndroidTest.xml
+++ b/tests/BootImageProfileTest/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for BootImageProfileTest">
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
      <!-- do not use DeviceSetup#set-property because it reboots the device b/136200738.
          furthermore the changes in /data/local.prop don't actually seem to get picked up.
     -->
diff --git a/tests/InputScreenshotTest/assets/phone/light_landscape_layout-preview.png b/tests/InputScreenshotTest/assets/phone/light_landscape_layout-preview.png
index 70e4a71..443de8e 100644
--- a/tests/InputScreenshotTest/assets/phone/light_landscape_layout-preview.png
+++ b/tests/InputScreenshotTest/assets/phone/light_landscape_layout-preview.png
Binary files differ
diff --git a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-ansi.png b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-ansi.png
index 502c1b4..cb69c0e 100644
--- a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-ansi.png
+++ b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-ansi.png
Binary files differ
diff --git a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-jis.png b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-jis.png
index 591b2fa..1c6d1b3 100644
--- a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-jis.png
+++ b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-jis.png
Binary files differ
diff --git a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview.png b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview.png
index 0137a85..c51da05 100644
--- a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview.png
+++ b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview.png
Binary files differ
diff --git a/tests/InputScreenshotTest/assets/tablet/dark_portrait_layout-preview.png b/tests/InputScreenshotTest/assets/tablet/dark_portrait_layout-preview.png
index 37a91e1..ab23401 100644
--- a/tests/InputScreenshotTest/assets/tablet/dark_portrait_layout-preview.png
+++ b/tests/InputScreenshotTest/assets/tablet/dark_portrait_layout-preview.png
Binary files differ