Merge "Add keyboard shortcut for desktop mode" 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/LSE_APP_COMPAT_OWNERS b/LSE_APP_COMPAT_OWNERS
new file mode 100644
index 0000000..3db0cd4
--- /dev/null
+++ b/LSE_APP_COMPAT_OWNERS
@@ -0,0 +1,6 @@
+# Owners for the App Compat flags (large_screen_experiences_app_compat)
+mcarli@google.com
+eevlachavas@google.com
+gracielawputri@google.com
+minagranic@google.com
+mariiasand@google.com
diff --git a/core/api/current.txt b/core/api/current.txt
index 4a2abf6..bdfe388 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -7024,6 +7024,7 @@
     method public void deleteNotificationChannelGroup(String);
     method public android.service.notification.StatusBarNotification[] getActiveNotifications();
     method public android.app.AutomaticZenRule getAutomaticZenRule(String);
+    method @FlaggedApi("android.app.modes_api") public int getAutomaticZenRuleState(@NonNull String);
     method public java.util.Map<java.lang.String,android.app.AutomaticZenRule> getAutomaticZenRules();
     method public int getBubblePreference();
     method @NonNull public android.app.NotificationManager.Policy getConsolidatedNotificationPolicy();
@@ -10088,7 +10089,7 @@
     method public CharSequence coerceToText(android.content.Context);
     method public String getHtmlText();
     method public android.content.Intent getIntent();
-    method @FlaggedApi("com.android.window.flags.delegate_unhandled_drags") @Nullable public android.app.PendingIntent getPendingIntent();
+    method @FlaggedApi("com.android.window.flags.delegate_unhandled_drags") @Nullable public android.content.IntentSender getIntentSender();
     method public CharSequence getText();
     method @Nullable public android.view.textclassifier.TextLinks getTextLinks();
     method public android.net.Uri getUri();
@@ -10099,7 +10100,7 @@
     method @FlaggedApi("com.android.window.flags.delegate_unhandled_drags") @NonNull public android.content.ClipData.Item build();
     method @FlaggedApi("com.android.window.flags.delegate_unhandled_drags") @NonNull public android.content.ClipData.Item.Builder setHtmlText(@Nullable String);
     method @FlaggedApi("com.android.window.flags.delegate_unhandled_drags") @NonNull public android.content.ClipData.Item.Builder setIntent(@Nullable android.content.Intent);
-    method @FlaggedApi("com.android.window.flags.delegate_unhandled_drags") @NonNull public android.content.ClipData.Item.Builder setPendingIntent(@Nullable android.app.PendingIntent);
+    method @FlaggedApi("com.android.window.flags.delegate_unhandled_drags") @NonNull public android.content.ClipData.Item.Builder setIntentSender(@Nullable android.content.IntentSender);
     method @FlaggedApi("com.android.window.flags.delegate_unhandled_drags") @NonNull public android.content.ClipData.Item.Builder setText(@Nullable CharSequence);
     method @FlaggedApi("com.android.window.flags.delegate_unhandled_drags") @NonNull public android.content.ClipData.Item.Builder setUri(@Nullable android.net.Uri);
   }
@@ -53136,7 +53137,7 @@
     field public static final int DRAG_FLAG_GLOBAL_URI_READ = 1; // 0x1
     field public static final int DRAG_FLAG_GLOBAL_URI_WRITE = 2; // 0x2
     field public static final int DRAG_FLAG_OPAQUE = 512; // 0x200
-    field @FlaggedApi("com.android.window.flags.delegate_unhandled_drags") public static final int DRAG_FLAG_START_PENDING_INTENT_ON_UNHANDLED_DRAG = 8192; // 0x2000
+    field @FlaggedApi("com.android.window.flags.delegate_unhandled_drags") public static final int DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG = 8192; // 0x2000
     field @Deprecated public static final int DRAWING_CACHE_QUALITY_AUTO = 0; // 0x0
     field @Deprecated public static final int DRAWING_CACHE_QUALITY_HIGH = 1048576; // 0x100000
     field @Deprecated public static final int DRAWING_CACHE_QUALITY_LOW = 524288; // 0x80000
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index a5e62ac..627b703 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -2276,6 +2276,7 @@
     method @RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public void processRequestStreaming(@NonNull android.app.ondeviceintelligence.Feature, @NonNull android.app.ondeviceintelligence.Content, int, @Nullable android.os.CancellationSignal, @Nullable android.app.ondeviceintelligence.ProcessingSignal, @NonNull java.util.concurrent.Executor, @NonNull android.app.ondeviceintelligence.StreamingResponseReceiver<android.app.ondeviceintelligence.Content,android.app.ondeviceintelligence.Content,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException>);
     method @RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public void requestFeatureDownload(@NonNull android.app.ondeviceintelligence.Feature, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.app.ondeviceintelligence.DownloadCallback);
     method @RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public void requestTokenCount(@NonNull android.app.ondeviceintelligence.Feature, @NonNull android.app.ondeviceintelligence.Content, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Long,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException>);
+    field public static final String API_VERSION_BUNDLE_KEY = "ApiVersionBundleKey";
     field public static final int REQUEST_TYPE_EMBEDDINGS = 2; // 0x2
     field public static final int REQUEST_TYPE_INFERENCE = 0; // 0x0
     field public static final int REQUEST_TYPE_PREPARE = 1; // 0x1
@@ -12957,6 +12958,34 @@
 
 }
 
+package android.service.ondeviceintelligence {
+
+  @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public abstract class OnDeviceIntelligenceService extends android.app.Service {
+    ctor public OnDeviceIntelligenceService();
+    method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
+    method public abstract void onDownloadFeature(@NonNull android.app.ondeviceintelligence.Feature, @Nullable android.os.CancellationSignal, @NonNull android.app.ondeviceintelligence.DownloadCallback);
+    method public abstract void onGetFeature(int, @NonNull android.os.OutcomeReceiver<android.app.ondeviceintelligence.Feature,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException>);
+    method public abstract void onGetFeatureDetails(@NonNull android.app.ondeviceintelligence.Feature, @NonNull android.os.OutcomeReceiver<android.app.ondeviceintelligence.FeatureDetails,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException>);
+    method public abstract void onGetReadOnlyFeatureFileDescriptorMap(@NonNull android.app.ondeviceintelligence.Feature, @NonNull java.util.function.Consumer<java.util.Map<java.lang.String,android.os.ParcelFileDescriptor>>);
+    method public abstract void onGetVersion(@NonNull java.util.function.LongConsumer);
+    method public abstract void onListFeatures(@NonNull android.os.OutcomeReceiver<java.util.List<android.app.ondeviceintelligence.Feature>,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException>);
+    field public static final String SERVICE_INTERFACE = "android.service.ondeviceintelligence.OnDeviceIntelligenceService";
+  }
+
+  @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public abstract class OnDeviceTrustedInferenceService extends android.app.Service {
+    ctor public OnDeviceTrustedInferenceService();
+    method public final void fetchFeatureFileInputStreamMap(@NonNull android.app.ondeviceintelligence.Feature, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.Map<java.lang.String,java.io.FileInputStream>>);
+    method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
+    method @NonNull public abstract void onCountTokens(@NonNull android.app.ondeviceintelligence.Feature, @NonNull android.app.ondeviceintelligence.Content, @Nullable android.os.CancellationSignal, @NonNull android.os.OutcomeReceiver<java.lang.Long,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException>);
+    method @NonNull public abstract void onProcessRequest(@NonNull android.app.ondeviceintelligence.Feature, @NonNull android.app.ondeviceintelligence.Content, int, @Nullable android.os.CancellationSignal, @Nullable android.app.ondeviceintelligence.ProcessingSignal, @NonNull android.os.OutcomeReceiver<android.app.ondeviceintelligence.Content,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException>);
+    method @NonNull public abstract void onProcessRequestStreaming(@NonNull android.app.ondeviceintelligence.Feature, @NonNull android.app.ondeviceintelligence.Content, int, @Nullable android.os.CancellationSignal, @Nullable android.app.ondeviceintelligence.ProcessingSignal, @NonNull android.app.ondeviceintelligence.StreamingResponseReceiver<android.app.ondeviceintelligence.Content,android.app.ondeviceintelligence.Content,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException>);
+    method public final java.io.FileInputStream openFileInput(@NonNull String) throws java.io.FileNotFoundException;
+    method public final void openFileInputAsync(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.io.FileInputStream>) throws java.io.FileNotFoundException;
+    field public static final String SERVICE_INTERFACE = "android.service.ondeviceintelligence.OnDeviceTrustedInferenceService";
+  }
+
+}
+
 package android.service.persistentdata {
 
   @FlaggedApi("android.security.frp_enforcement") public class PersistentDataBlockManager {
diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt
index ccfb17a..1923641 100644
--- a/core/api/system-lint-baseline.txt
+++ b/core/api/system-lint-baseline.txt
@@ -509,6 +509,12 @@
     Methods must not throw generic exceptions (`java.lang.Throwable`)
 
 
+InvalidNullabilityOverride: android.service.ondeviceintelligence.OnDeviceIntelligenceService#onBind(android.content.Intent) parameter #0:
+    Invalid nullability on parameter `intent` in method `onBind`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
+InvalidNullabilityOverride: android.service.ondeviceintelligence.OnDeviceTrustedInferenceService#onBind(android.content.Intent) parameter #0:
+    Invalid nullability on parameter `intent` in method `onBind`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
+InvalidNullabilityOverride: android.service.ondeviceintelligence.OnDeviceTrustedInferenceService#openFileInput(String) parameter #0:
+    Invalid nullability on parameter `filename` in method `openFileInput`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
 InvalidNullabilityOverride: android.service.textclassifier.TextClassifierService#onUnbind(android.content.Intent) parameter #0:
     Invalid nullability on parameter `intent` in method `onUnbind`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
 InvalidNullabilityOverride: android.service.voice.HotwordDetectionService#getSystemService(String) parameter #0:
@@ -565,6 +571,8 @@
     Missing nullability on parameter `args` in method `dump`
 MissingNullability: android.service.notification.NotificationAssistantService#attachBaseContext(android.content.Context) parameter #0:
     Missing nullability on parameter `base` in method `attachBaseContext`
+MissingNullability: android.service.ondeviceintelligence.OnDeviceTrustedInferenceService#openFileInput(String):
+    Missing nullability on method `openFileInput` return
 MissingNullability: android.telephony.NetworkService#onUnbind(android.content.Intent) parameter #0:
     Missing nullability on parameter `intent` in method `onUnbind`
 MissingNullability: android.telephony.data.DataService#onUnbind(android.content.Intent) parameter #0:
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/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index b5e3556..8f81ae2 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -225,6 +225,7 @@
     boolean removeAutomaticZenRule(String id, boolean fromUser);
     boolean removeAutomaticZenRules(String packageName, boolean fromUser);
     int getRuleInstanceCount(in ComponentName owner);
+    int getAutomaticZenRuleState(String id);
     void setAutomaticZenRuleState(String id, in Condition condition);
 
     byte[] getBackupPayload(int user);
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/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 9dfb5b0..d49a254 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1406,6 +1406,26 @@
     }
 
     /**
+     * Returns the current activation state of an {@link AutomaticZenRule}.
+     *
+     * <p>Returns {@link Condition#STATE_UNKNOWN} if the rule does not exist or the calling
+     * package doesn't have access to it.
+     *
+     * @param id The id of the rule
+     * @return the state of the rule.
+     */
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    @Condition.State
+    public int getAutomaticZenRuleState(@NonNull String id) {
+        INotificationManager service = getService();
+        try {
+            return service.getAutomaticZenRuleState(id);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Informs the notification manager that the state of an {@link AutomaticZenRule} has changed.
      * Use this method to put the system into Do Not Disturb mode or request that it exits Do Not
      * Disturb mode. The calling app must own the provided {@link android.app.AutomaticZenRule}.
diff --git a/core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java b/core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java
index d34ab60..4d8e0d5 100644
--- a/core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java
+++ b/core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java
@@ -60,7 +60,7 @@
 @SystemService(Context.ON_DEVICE_INTELLIGENCE_SERVICE)
 @FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE)
 public class OnDeviceIntelligenceManager {
-    private static final String API_VERSION_BUNDLE_KEY = "ApiVersionBundleKey";
+    public static final String API_VERSION_BUNDLE_KEY = "ApiVersionBundleKey";
     private final Context mContext;
     private final IOnDeviceIntelligenceManager mService;
 
@@ -86,6 +86,10 @@
         //  version and package name of the remote service implementing this.
         try {
             RemoteCallback callback = new RemoteCallback(result -> {
+                if (result == null) {
+                    Binder.withCleanCallingIdentity(
+                            () -> callbackExecutor.execute(() -> versionConsumer.accept(0)));
+                }
                 long version = result.getLong(API_VERSION_BUNDLE_KEY);
                 Binder.withCleanCallingIdentity(
                         () -> callbackExecutor.execute(() -> versionConsumer.accept(version)));
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/content/ClipData.java b/core/java/android/content/ClipData.java
index eb357fe..728c350 100644
--- a/core/java/android/content/ClipData.java
+++ b/core/java/android/content/ClipData.java
@@ -26,8 +26,6 @@
 import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SuppressLint;
-import android.app.PendingIntent;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.pm.ActivityInfo;
 import android.content.res.AssetFileDescriptor;
@@ -213,7 +211,7 @@
         final CharSequence mText;
         final String mHtmlText;
         final Intent mIntent;
-        final PendingIntent mPendingIntent;
+        final IntentSender mIntentSender;
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
         Uri mUri;
         private TextLinks mTextLinks;
@@ -225,12 +223,11 @@
          * A builder for a ClipData Item.
          */
         @FlaggedApi(FLAG_DELEGATE_UNHANDLED_DRAGS)
-        @SuppressLint("PackageLayering")
         public static final class Builder {
             private CharSequence mText;
             private String mHtmlText;
             private Intent mIntent;
-            private PendingIntent mPendingIntent;
+            private IntentSender mIntentSender;
             private Uri mUri;
 
             /**
@@ -264,18 +261,20 @@
             }
 
             /**
-             * Sets the PendingIntent for the item to be constructed. To prevent receiving apps from
-             * improperly manipulating the intent to launch another activity as this caller, the
-             * provided PendingIntent must be immutable (see {@link PendingIntent#FLAG_IMMUTABLE}).
-             * The system will clean up the PendingIntent when it is no longer used.
+             * Sets the {@link IntentSender} for the item to be constructed. To prevent receiving
+             * apps from improperly manipulating the intent to launch another activity as this
+             * caller, the provided IntentSender must be immutable.
+             *
+             * If there is a fixed lifetime for this ClipData (ie. for drag and drop), the system
+             * will cancel the IntentSender when it is no longer used.
              */
             @FlaggedApi(FLAG_DELEGATE_UNHANDLED_DRAGS)
             @NonNull
-            public Builder setPendingIntent(@Nullable PendingIntent pendingIntent) {
-                if (pendingIntent != null && !pendingIntent.isImmutable()) {
-                    throw new IllegalArgumentException("Expected pending intent to be immutable");
+            public Builder setIntentSender(@Nullable IntentSender intentSender) {
+                if (intentSender != null && !intentSender.isImmutable()) {
+                    throw new IllegalArgumentException("Expected intent sender to be immutable");
                 }
-                mPendingIntent = pendingIntent;
+                mIntentSender = intentSender;
                 return this;
             }
 
@@ -295,7 +294,7 @@
             @FlaggedApi(FLAG_DELEGATE_UNHANDLED_DRAGS)
             @NonNull
             public Item build() {
-                return new Item(mText, mHtmlText, mIntent, mPendingIntent, mUri);
+                return new Item(mText, mHtmlText, mIntent, mIntentSender, mUri);
             }
         }
 
@@ -305,7 +304,7 @@
             mText = other.mText;
             mHtmlText = other.mHtmlText;
             mIntent = other.mIntent;
-            mPendingIntent = other.mPendingIntent;
+            mIntentSender = other.mIntentSender;
             mUri = other.mUri;
             mActivityInfo = other.mActivityInfo;
             mTextLinks = other.mTextLinks;
@@ -366,7 +365,7 @@
         /**
          * Builder ctor.
          */
-        private Item(CharSequence text, String htmlText, Intent intent, PendingIntent pendingIntent,
+        private Item(CharSequence text, String htmlText, Intent intent, IntentSender intentSender,
                 Uri uri) {
             if (htmlText != null && text == null) {
                 throw new IllegalArgumentException(
@@ -375,7 +374,7 @@
             mText = text;
             mHtmlText = htmlText;
             mIntent = intent;
-            mPendingIntent = pendingIntent;
+            mIntentSender = intentSender;
             mUri = uri;
         }
 
@@ -401,12 +400,12 @@
         }
 
         /**
-         * Returns the pending intent in this Item.
+         * Returns the {@link IntentSender} in this Item.
          */
         @FlaggedApi(FLAG_DELEGATE_UNHANDLED_DRAGS)
         @Nullable
-        public PendingIntent getPendingIntent() {
-            return mPendingIntent;
+        public IntentSender getIntentSender() {
+            return mIntentSender;
         }
 
         /**
@@ -1131,35 +1130,6 @@
     }
 
     /**
-     * Checks if this clip data has a pending intent that is an activity type.
-     * @hide
-     */
-    public boolean hasActivityPendingIntents() {
-        final int size = mItems.size();
-        for (int i = 0; i < size; i++) {
-            final Item item = mItems.get(i);
-            if (item.mPendingIntent != null && item.mPendingIntent.isActivity()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Cleans up all pending intents in the ClipData.
-     * @hide
-     */
-    public void cleanUpPendingIntents() {
-        final int size = mItems.size();
-        for (int i = 0; i < size; i++) {
-            final Item item = mItems.get(i);
-            if (item.mPendingIntent != null) {
-                item.mPendingIntent.cancel();
-            }
-        }
-    }
-
-    /**
      * Prepare this {@link ClipData} to leave an app process.
      *
      * @hide
@@ -1361,7 +1331,7 @@
             TextUtils.writeToParcel(item.mText, dest, flags);
             dest.writeString8(item.mHtmlText);
             dest.writeTypedObject(item.mIntent, flags);
-            dest.writeTypedObject(item.mPendingIntent, flags);
+            dest.writeTypedObject(item.mIntentSender, flags);
             dest.writeTypedObject(item.mUri, flags);
             dest.writeTypedObject(mParcelItemActivityInfos ? item.mActivityInfo : null, flags);
             dest.writeTypedObject(item.mTextLinks, flags);
@@ -1381,11 +1351,11 @@
             CharSequence text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
             String htmlText = in.readString8();
             Intent intent = in.readTypedObject(Intent.CREATOR);
-            PendingIntent pendingIntent = in.readTypedObject(PendingIntent.CREATOR);
+            IntentSender intentSender = in.readTypedObject(IntentSender.CREATOR);
             Uri uri = in.readTypedObject(Uri.CREATOR);
             ActivityInfo info = in.readTypedObject(ActivityInfo.CREATOR);
             TextLinks textLinks = in.readTypedObject(TextLinks.CREATOR);
-            Item item = new Item(text, htmlText, intent, pendingIntent, uri);
+            Item item = new Item(text, htmlText, intent, intentSender, uri);
             item.setActivityInfo(info);
             item.setTextLinks(textLinks);
             mItems.add(item);
diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl
index 62db65f..cec49c7 100644
--- a/core/java/android/content/pm/ILauncherApps.aidl
+++ b/core/java/android/content/pm/ILauncherApps.aidl
@@ -130,4 +130,6 @@
     void unRegisterDumpCallback(IDumpCallback cb);
 
     void setArchiveCompatibilityOptions(boolean enableIconOverlay, boolean enableUnarchivalConfirmation);
+
+    List<UserHandle> getUserProfiles();
 }
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 9c859c4..e437925 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -695,12 +695,23 @@
      * Otherwise it'll return the same list as {@link UserManager#getUserProfiles()} would.
      */
     public List<UserHandle> getProfiles() {
-        if (mUserManager.isManagedProfile()) {
-            // If it's a managed profile, only return the current profile.
-            final List result =  new ArrayList(1);
+        if (mUserManager.isManagedProfile()
+                || (android.multiuser.Flags.enableLauncherAppsHiddenProfileChecks()
+                        && android.os.Flags.allowPrivateProfile()
+                        && mUserManager.isPrivateProfile())) {
+            // If it's a managed or private profile, only return the current profile.
+            final List result = new ArrayList(1);
             result.add(android.os.Process.myUserHandle());
             return result;
         } else {
+            if (android.multiuser.Flags.enableLauncherAppsHiddenProfileChecks()) {
+                try {
+                    return mService.getUserProfiles();
+                } catch (RemoteException re) {
+                    throw re.rethrowFromSystemServer();
+                }
+            }
+
             return mUserManager.getUserProfiles();
         }
     }
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/service/ondeviceintelligence/IOnDeviceIntelligenceService.aidl b/core/java/android/service/ondeviceintelligence/IOnDeviceIntelligenceService.aidl
new file mode 100644
index 0000000..bbb4bc6
--- /dev/null
+++ b/core/java/android/service/ondeviceintelligence/IOnDeviceIntelligenceService.aidl
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.ondeviceintelligence;
+
+import android.os.PersistableBundle;
+import android.os.ParcelFileDescriptor;
+import android.os.ICancellationSignal;
+import android.os.RemoteCallback;
+import android.app.ondeviceintelligence.IDownloadCallback;
+import android.app.ondeviceintelligence.Feature;
+import android.app.ondeviceintelligence.IFeatureCallback;
+import android.app.ondeviceintelligence.IListFeaturesCallback;
+import android.app.ondeviceintelligence.IFeatureDetailsCallback;
+import com.android.internal.infra.AndroidFuture;
+
+
+/**
+ * Interface for a concrete implementation to provide on device intelligence services.
+ *
+ * @hide
+ */
+oneway interface IOnDeviceIntelligenceService {
+    void getVersion(in RemoteCallback remoteCallback);
+    void getFeature(in int featureId, in IFeatureCallback featureCallback);
+    void listFeatures(in IListFeaturesCallback listFeaturesCallback);
+    void getFeatureDetails(in Feature feature, in IFeatureDetailsCallback featureDetailsCallback);
+    void getReadOnlyFileDescriptor(in String fileName, in AndroidFuture<ParcelFileDescriptor> future);
+    void getReadOnlyFeatureFileDescriptorMap(in Feature feature, in RemoteCallback remoteCallback);
+    void requestFeatureDownload(in Feature feature, in ICancellationSignal cancellationSignal, in IDownloadCallback downloadCallback);
+}
\ No newline at end of file
diff --git a/core/java/android/service/ondeviceintelligence/IOnDeviceTrustedInferenceService.aidl b/core/java/android/service/ondeviceintelligence/IOnDeviceTrustedInferenceService.aidl
new file mode 100644
index 0000000..08eb927
--- /dev/null
+++ b/core/java/android/service/ondeviceintelligence/IOnDeviceTrustedInferenceService.aidl
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.ondeviceintelligence;
+
+import android.app.ondeviceintelligence.IStreamingResponseCallback;
+import android.app.ondeviceintelligence.IResponseCallback;
+import android.app.ondeviceintelligence.ITokenCountCallback;
+import android.app.ondeviceintelligence.IProcessingSignal;
+import android.app.ondeviceintelligence.Content;
+import android.app.ondeviceintelligence.Feature;
+import android.os.ICancellationSignal;
+import android.service.ondeviceintelligence.IRemoteStorageService;
+
+
+/**
+ * Interface for a concrete implementation to provide on device trusted inference.
+ *
+ * @hide
+ */
+oneway interface IOnDeviceTrustedInferenceService {
+    void registerRemoteStorageService(in IRemoteStorageService storageService);
+    void requestTokenCount(in Feature feature, in Content request, in ICancellationSignal cancellationSignal,
+                            in ITokenCountCallback tokenCountCallback);
+    void processRequest(in Feature feature, in Content request, in int requestType,
+                        in ICancellationSignal cancellationSignal, in IProcessingSignal processingSignal,
+                        in IResponseCallback callback);
+    void processRequestStreaming(in Feature feature, in Content request, in int requestType,
+                                in ICancellationSignal cancellationSignal, in IProcessingSignal processingSignal,
+                                in IStreamingResponseCallback callback);
+}
\ No newline at end of file
diff --git a/core/java/android/service/ondeviceintelligence/IRemoteStorageService.aidl b/core/java/android/service/ondeviceintelligence/IRemoteStorageService.aidl
new file mode 100644
index 0000000..a6f49e1
--- /dev/null
+++ b/core/java/android/service/ondeviceintelligence/IRemoteStorageService.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.ondeviceintelligence;
+
+import android.app.ondeviceintelligence.Feature;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteCallback;
+
+import com.android.internal.infra.AndroidFuture;
+
+/**
+ * Interface for a concrete implementation to provide access to storage read access
+ * for the isolated process.
+ *
+ * @hide
+ */
+oneway interface IRemoteStorageService {
+    void getReadOnlyFileDescriptor(in String filePath, in AndroidFuture<ParcelFileDescriptor> future);
+    void getReadOnlyFeatureFileDescriptorMap(in Feature feature, in RemoteCallback remoteCallback);
+}
\ No newline at end of file
diff --git a/core/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java b/core/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java
new file mode 100644
index 0000000..0cba1d3
--- /dev/null
+++ b/core/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.app.ondeviceintelligence.DownloadCallback;
+import android.app.ondeviceintelligence.Feature;
+import android.app.ondeviceintelligence.FeatureDetails;
+import android.app.ondeviceintelligence.IDownloadCallback;
+import android.app.ondeviceintelligence.IFeatureCallback;
+import android.app.ondeviceintelligence.IFeatureDetailsCallback;
+import android.app.ondeviceintelligence.IListFeaturesCallback;
+import android.app.ondeviceintelligence.OnDeviceIntelligenceManager;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.IBinder;
+import android.os.ICancellationSignal;
+import android.os.OutcomeReceiver;
+import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.internal.infra.AndroidFuture;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.Consumer;
+import java.util.function.LongConsumer;
+
+/**
+ * Abstract base class for performing setup for on-device inference and providing file access to
+ * the isolated counter part {@link OnDeviceTrustedInferenceService}.
+ *
+ * <p> A service that provides configuration and model files relevant to performing inference on
+ * device. The system's default OnDeviceIntelligenceService implementation is configured in
+ * {@code config_defaultOnDeviceIntelligenceService}. If this config has no value, a stub is
+ * returned.
+ *
+ * <pre>
+ * {@literal
+ * <service android:name=".SampleOnDeviceIntelligenceService"
+ *          android:permission="android.permission.BIND_ON_DEVICE_INTELLIGENCE_SERVICE">
+ * </service>}
+ * </pre>
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE)
+public abstract class OnDeviceIntelligenceService extends Service {
+    private static final String TAG = OnDeviceIntelligenceService.class.getSimpleName();
+
+    /**
+     * The {@link Intent} that must be declared as handled by the service. To be supported, the
+     * service must also require the
+     * {@link android.Manifest.permission#BIND_ON_DEVICE_INTELLIGENCE_SERVICE}
+     * permission so that other applications can not abuse it.
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+    public static final String SERVICE_INTERFACE =
+            "android.service.ondeviceintelligence.OnDeviceIntelligenceService";
+
+    /**
+     * @hide
+     */
+    @Nullable
+    @Override
+    public final IBinder onBind(@NonNull Intent intent) {
+        if (SERVICE_INTERFACE.equals(intent.getAction())) {
+            return new IOnDeviceIntelligenceService.Stub() {
+                /** {@inheritDoc} */
+                @Override
+                public void getVersion(RemoteCallback remoteCallback) {
+                    Objects.requireNonNull(remoteCallback);
+                    OnDeviceIntelligenceService.this.onGetVersion(l -> {
+                        Bundle b = new Bundle();
+                        b.putLong(OnDeviceIntelligenceManager.API_VERSION_BUNDLE_KEY, l);
+                        remoteCallback.sendResult(b);
+                    });
+                }
+
+                @Override
+                public void listFeatures(IListFeaturesCallback listFeaturesCallback) {
+                    Objects.requireNonNull(listFeaturesCallback);
+                    OnDeviceIntelligenceService.this.onListFeatures(
+                            wrapListFeaturesCallback(listFeaturesCallback));
+                }
+
+                @Override
+                public void getFeature(int id, IFeatureCallback featureCallback) {
+                    Objects.requireNonNull(featureCallback);
+                    OnDeviceIntelligenceService.this.onGetFeature(id,
+                            wrapFeatureCallback(featureCallback));
+                }
+
+
+                @Override
+                public void getFeatureDetails(Feature feature,
+                        IFeatureDetailsCallback featureDetailsCallback) {
+                    Objects.requireNonNull(feature);
+                    Objects.requireNonNull(featureDetailsCallback);
+
+                    OnDeviceIntelligenceService.this.onGetFeatureDetails(feature,
+                            wrapFeatureDetailsCallback(featureDetailsCallback));
+                }
+
+                @Override
+                public void requestFeatureDownload(Feature feature,
+                        ICancellationSignal cancellationSignal,
+                        IDownloadCallback downloadCallback) {
+                    Objects.requireNonNull(feature);
+                    Objects.requireNonNull(downloadCallback);
+
+                    OnDeviceIntelligenceService.this.onDownloadFeature(feature,
+                            CancellationSignal.fromTransport(cancellationSignal),
+                            wrapDownloadCallback(downloadCallback));
+                }
+
+                @Override
+                public void getReadOnlyFileDescriptor(String fileName,
+                        AndroidFuture<ParcelFileDescriptor> future) {
+                    Objects.requireNonNull(fileName);
+                    Objects.requireNonNull(future);
+
+                    OnDeviceIntelligenceService.this.onGetReadOnlyFileDescriptor(fileName,
+                            future);
+                }
+
+                @Override
+                public void getReadOnlyFeatureFileDescriptorMap(
+                        Feature feature, RemoteCallback remoteCallback) {
+                    Objects.requireNonNull(feature);
+                    Objects.requireNonNull(remoteCallback);
+
+                    OnDeviceIntelligenceService.this.onGetReadOnlyFeatureFileDescriptorMap(
+                            feature, parcelFileDescriptorMap -> {
+                                Bundle bundle = new Bundle();
+                                parcelFileDescriptorMap.forEach(bundle::putParcelable);
+                                remoteCallback.sendResult(bundle);
+                            });
+                }
+            };
+        }
+        Slog.w(TAG, "Incorrect service interface, returning null.");
+        return null;
+    }
+
+    private OutcomeReceiver<Feature,
+            OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException> wrapFeatureCallback(
+            IFeatureCallback featureCallback) {
+        return new OutcomeReceiver<>() {
+            @Override
+            public void onResult(@NonNull Feature feature) {
+                try {
+                    featureCallback.onSuccess(feature);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Error sending feature: " + e);
+                }
+            }
+
+            @Override
+            public void onError(
+                    @NonNull OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException exception) {
+                try {
+                    featureCallback.onFailure(exception.getErrorCode(), exception.getMessage(),
+                            exception.getErrorParams());
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Error sending download feature: " + e);
+                }
+            }
+        };
+
+    }
+
+    private OutcomeReceiver<List<Feature>,
+            OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException> wrapListFeaturesCallback(
+            IListFeaturesCallback listFeaturesCallback) {
+        return new OutcomeReceiver<>() {
+            @Override
+            public void onResult(@NonNull List<Feature> features) {
+                try {
+                    listFeaturesCallback.onSuccess(features);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Error sending feature: " + e);
+                }
+            }
+
+            @Override
+            public void onError(
+                    @NonNull OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException exception) {
+                try {
+                    listFeaturesCallback.onFailure(exception.getErrorCode(), exception.getMessage(),
+                            exception.getErrorParams());
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Error sending download feature: " + e);
+                }
+            }
+        };
+    }
+
+    private OutcomeReceiver<FeatureDetails,
+            OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException> wrapFeatureDetailsCallback(
+            IFeatureDetailsCallback featureStatusCallback) {
+        return new OutcomeReceiver<>() {
+            @Override
+            public void onResult(FeatureDetails result) {
+                try {
+                    featureStatusCallback.onSuccess(result);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Error sending feature status: " + e);
+                }
+            }
+
+            @Override
+            public void onError(
+                    @NonNull OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException exception) {
+                try {
+                    featureStatusCallback.onFailure(exception.getErrorCode(),
+                            exception.getMessage(), exception.getErrorParams());
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Error sending feature status: " + e);
+                }
+            }
+        };
+    }
+
+
+    private DownloadCallback wrapDownloadCallback(IDownloadCallback downloadCallback) {
+        return new DownloadCallback() {
+            @Override
+            public void onDownloadStarted(long bytesToDownload) {
+                try {
+                    downloadCallback.onDownloadStarted(bytesToDownload);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Error sending download status: " + e);
+                }
+            }
+
+            @Override
+            public void onDownloadFailed(int failureStatus,
+                    String errorMessage, @NonNull PersistableBundle errorParams) {
+                try {
+                    downloadCallback.onDownloadFailed(failureStatus, errorMessage, errorParams);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Error sending download status: " + e);
+                }
+            }
+
+            @Override
+            public void onDownloadProgress(long totalBytesDownloaded) {
+                try {
+                    downloadCallback.onDownloadProgress(totalBytesDownloaded);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Error sending download status: " + e);
+                }
+            }
+
+            @Override
+            public void onDownloadCompleted(@NonNull PersistableBundle persistableBundle) {
+                try {
+                    downloadCallback.onDownloadCompleted(persistableBundle);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Error sending download status: " + e);
+                }
+            }
+        };
+    }
+
+    private void onGetReadOnlyFileDescriptor(@NonNull String fileName,
+            @NonNull AndroidFuture<ParcelFileDescriptor> future) {
+        Slog.v(TAG, "onGetReadOnlyFileDescriptor " + fileName);
+        Binder.withCleanCallingIdentity(() -> {
+            Slog.v(TAG,
+                    "onGetReadOnlyFileDescriptor: " + fileName + " under internal app storage.");
+            File f = new File(getBaseContext().getFilesDir(), fileName);
+            ParcelFileDescriptor pfd = null;
+            try {
+                pfd = ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY);
+                Slog.d(TAG, "Successfully opened a file with ParcelFileDescriptor.");
+            } catch (FileNotFoundException e) {
+                Slog.e(TAG, "Cannot open file. No ParcelFileDescriptor returned.");
+            } finally {
+                future.complete(pfd);
+            }
+        });
+    }
+
+    /**
+     * Provide implementation for a scenario when caller wants to get all feature related
+     * file-descriptors that might be required for processing a request for the corresponding the
+     * feature.
+     *
+     * @param feature                   the feature for which files need to be opened.
+     * @param fileDescriptorMapConsumer callback to be populated with a map of file-path and
+     *                                  corresponding ParcelDescriptor to be used in a remote
+     *                                  service.
+     */
+    public abstract void onGetReadOnlyFeatureFileDescriptorMap(
+            @NonNull Feature feature,
+            @NonNull Consumer<Map<String, ParcelFileDescriptor>> fileDescriptorMapConsumer);
+
+    /**
+     * Request download for feature that is requested and listen to download progress updates. If
+     * the download completes successfully, success callback should be populated.
+     *
+     * @param feature            the feature for which files need to be downlaoded.
+     *                           process.
+     * @param cancellationSignal signal to attach a listener to, and receive cancellation signals
+     *                           from thw client.
+     * @param downloadCallback   callback to populate download updates for clients to listen on..
+     */
+    public abstract void onDownloadFeature(
+            @NonNull Feature feature,
+            @Nullable CancellationSignal cancellationSignal,
+            @NonNull DownloadCallback downloadCallback);
+
+    /**
+     * Provide feature details for the passed in feature. Usually the client and remote
+     * implementation use the {@link Feature#getFeatureParams()} as a hint to communicate what
+     * details the client is looking for.
+     *
+     * @param feature               the feature for which status needs to be known.
+     * @param featureStatusCallback callback to populate the resulting feature status.
+     */
+    public abstract void onGetFeatureDetails(@NonNull Feature feature,
+            @NonNull OutcomeReceiver<FeatureDetails,
+                    OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException> featureStatusCallback);
+
+
+    /**
+     * Get feature using the provided identifier to the remote implementation.
+     *
+     * @param featureCallback callback to populate the features list.
+     */
+    public abstract void onGetFeature(int featureId,
+            @NonNull OutcomeReceiver<Feature,
+                    OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException> featureCallback);
+
+    /**
+     * List all features which are available in the remote implementation. The implementation might
+     * choose to provide only a certain list of features based on the caller.
+     *
+     * @param listFeaturesCallback callback to populate the features list.
+     */
+    public abstract void onListFeatures(@NonNull OutcomeReceiver<List<Feature>,
+            OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException> listFeaturesCallback);
+
+    /**
+     * Provides a long value representing the version of the remote implementation processing
+     * requests.
+     *
+     * @param versionConsumer consumer to populate the version.
+     */
+    public abstract void onGetVersion(@NonNull LongConsumer versionConsumer);
+}
diff --git a/core/java/android/service/ondeviceintelligence/OnDeviceTrustedInferenceService.java b/core/java/android/service/ondeviceintelligence/OnDeviceTrustedInferenceService.java
new file mode 100644
index 0000000..96982e3
--- /dev/null
+++ b/core/java/android/service/ondeviceintelligence/OnDeviceTrustedInferenceService.java
@@ -0,0 +1,410 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.app.ondeviceintelligence.Content;
+import android.app.ondeviceintelligence.Feature;
+import android.app.ondeviceintelligence.IProcessingSignal;
+import android.app.ondeviceintelligence.IResponseCallback;
+import android.app.ondeviceintelligence.IStreamingResponseCallback;
+import android.app.ondeviceintelligence.ITokenCountCallback;
+import android.app.ondeviceintelligence.OnDeviceIntelligenceManager;
+import android.app.ondeviceintelligence.ProcessingSignal;
+import android.app.ondeviceintelligence.StreamingResponseReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.CancellationSignal;
+import android.os.IBinder;
+import android.os.ICancellationSignal;
+import android.os.OutcomeReceiver;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.infra.AndroidFuture;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * Abstract base class for performing inference in a isolated process. This service exposes its
+ * methods via {@link android.app.ondeviceintelligence.OnDeviceIntelligenceManager}.
+ *
+ * <p> A service that provides methods to perform on-device inference both in streaming and
+ * non-streaming fashion. Also, provides a way to register a storage service that will be used to
+ * read-only access files from the {@link OnDeviceIntelligenceService} counterpart. </p>
+ *
+ * <pre>
+ * {@literal
+ * <service android:name=".SampleTrustedInferenceService"
+ *          android:permission="android.permission.BIND_ONDEVICE_TRUSTED_INFERENCE_SERVICE"
+ *          android:isolatedProcess="true">
+ * </service>}
+ * </pre>
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE)
+public abstract class OnDeviceTrustedInferenceService extends Service {
+    private static final String TAG = OnDeviceTrustedInferenceService.class.getSimpleName();
+
+    /**
+     * The {@link Intent} that must be declared as handled by the service. To be supported, the
+     * service must also require the
+     * {@link android.Manifest.permission#BIND_ON_DEVICE_TRUSTED_INFERENCE_SERVICE}
+     * permission so that other applications can not abuse it.
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+    public static final String SERVICE_INTERFACE =
+            "android.service.ondeviceintelligence.OnDeviceTrustedInferenceService";
+
+    private IRemoteStorageService mRemoteStorageService;
+
+    /**
+     * @hide
+     */
+    @Nullable
+    @Override
+    public final IBinder onBind(@NonNull Intent intent) {
+        if (SERVICE_INTERFACE.equals(intent.getAction())) {
+            return new IOnDeviceTrustedInferenceService.Stub() {
+                @Override
+                public void registerRemoteStorageService(IRemoteStorageService storageService) {
+                    Objects.requireNonNull(storageService);
+                    mRemoteStorageService = storageService;
+                }
+
+                @Override
+                public void requestTokenCount(Feature feature, Content request,
+                        ICancellationSignal cancellationSignal,
+                        ITokenCountCallback tokenCountCallback) {
+                    Objects.requireNonNull(feature);
+                    Objects.requireNonNull(tokenCountCallback);
+                    OnDeviceTrustedInferenceService.this.onCountTokens(feature,
+                            request,
+                            CancellationSignal.fromTransport(cancellationSignal),
+                            wrapTokenCountCallback(tokenCountCallback));
+                }
+
+                @Override
+                public void processRequestStreaming(Feature feature, Content request,
+                        int requestType, ICancellationSignal cancellationSignal,
+                        IProcessingSignal processingSignal,
+                        IStreamingResponseCallback callback) {
+                    Objects.requireNonNull(feature);
+                    Objects.requireNonNull(request);
+                    Objects.requireNonNull(callback);
+
+                    OnDeviceTrustedInferenceService.this.onProcessRequestStreaming(feature,
+                            request,
+                            requestType,
+                            CancellationSignal.fromTransport(cancellationSignal),
+                            ProcessingSignal.fromTransport(processingSignal),
+                            wrapStreamingResponseCallback(callback)
+                    );
+                }
+
+                @Override
+                public void processRequest(Feature feature, Content request,
+                        int requestType, ICancellationSignal cancellationSignal,
+                        IProcessingSignal processingSignal,
+                        IResponseCallback callback) {
+                    Objects.requireNonNull(feature);
+                    Objects.requireNonNull(request);
+                    Objects.requireNonNull(callback);
+
+
+                    OnDeviceTrustedInferenceService.this.onProcessRequest(feature, request,
+                            requestType, CancellationSignal.fromTransport(cancellationSignal),
+                            ProcessingSignal.fromTransport(processingSignal),
+                            wrapResponseCallback(callback)
+                    );
+                }
+            };
+        }
+        Slog.w(TAG, "Incorrect service interface, returning null.");
+        return null;
+    }
+
+    /**
+     * Invoked when caller  wants to obtain a count of number of tokens present in the passed in
+     * Request associated with the provided feature.
+     * The expectation from the implementation is that when processing is complete, it
+     * should provide the token count in the {@link OutcomeReceiver#onResult}.
+     *
+     * @param feature            feature which is associated with the request.
+     * @param request            request that requires processing.
+     * @param cancellationSignal Cancellation Signal to receive cancellation events from client and
+     *                           configure a listener to.
+     * @param callback           callback to populate failure and full response for the provided
+     *                           request.
+     */
+    @NonNull
+    public abstract void onCountTokens(
+            @NonNull Feature feature,
+            @NonNull Content request,
+            @Nullable CancellationSignal cancellationSignal,
+            @NonNull OutcomeReceiver<Long,
+                    OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException> callback);
+
+    /**
+     * Invoked when caller provides a request for a particular feature to be processed in a
+     * streaming manner. The expectation from the implementation is that when processing the
+     * request,
+     * it periodically populates the {@link StreamingResponseReceiver#onNewContent} to continuously
+     * provide partial Content results for the caller to utilize. Optionally the implementation can
+     * provide the complete response in the {@link StreamingResponseReceiver#onResult} upon
+     * processing completion.
+     *
+     * @param feature            feature which is associated with the request.
+     * @param request            request that requires processing.
+     * @param requestType        identifier representing the type of request.
+     * @param cancellationSignal Cancellation Signal to receive cancellation events from client and
+     *                           configure a listener to.
+     * @param processingSignal   Signal to receive custom action instructions from client.
+     * @param callback           callback to populate the partial responses, failure and optionally
+     *                           full response for the provided request.
+     */
+    @NonNull
+    public abstract void onProcessRequestStreaming(
+            @NonNull Feature feature,
+            @NonNull Content request,
+            @OnDeviceIntelligenceManager.RequestType int requestType,
+            @Nullable CancellationSignal cancellationSignal,
+            @Nullable ProcessingSignal processingSignal,
+            @NonNull StreamingResponseReceiver<Content, Content,
+                    OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException> callback);
+
+    /**
+     * Invoked when caller provides a request for a particular feature to be processed in one shot
+     * completely.
+     * The expectation from the implementation is that when processing the request is complete, it
+     * should
+     * provide the complete response in the {@link OutcomeReceiver#onResult}.
+     *
+     * @param feature            feature which is associated with the request.
+     * @param request            request that requires processing.
+     * @param requestType        identifier representing the type of request.
+     * @param cancellationSignal Cancellation Signal to receive cancellation events from client and
+     *                           configure a listener to.
+     * @param processingSignal   Signal to receive custom action instructions from client.
+     * @param callback           callback to populate failure and full response for the provided
+     *                           request.
+     */
+    @NonNull
+    public abstract void onProcessRequest(
+            @NonNull Feature feature,
+            @NonNull Content request,
+            @OnDeviceIntelligenceManager.RequestType int requestType,
+            @Nullable CancellationSignal cancellationSignal,
+            @Nullable ProcessingSignal processingSignal,
+            @NonNull OutcomeReceiver<Content,
+                    OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException> callback);
+
+    /**
+     * Overrides {@link Context#openFileInput} to read files with the given file names under the
+     * internal app storage of the {@link OnDeviceIntelligenceService}, i.e., only files stored in
+     * {@link Context#getFilesDir()} can be opened.
+     */
+    @Override
+    public final FileInputStream openFileInput(@NonNull String filename) throws
+            FileNotFoundException {
+        try {
+            AndroidFuture<ParcelFileDescriptor> future = new AndroidFuture<>();
+            mRemoteStorageService.getReadOnlyFileDescriptor(filename, future);
+            ParcelFileDescriptor pfd = future.get();
+            return new FileInputStream(pfd.getFileDescriptor());
+        } catch (RemoteException | ExecutionException | InterruptedException e) {
+            Log.w(TAG, "Cannot open file due to remote service failure");
+            throw new FileNotFoundException(e.getMessage());
+        }
+    }
+
+    /**
+     * Provides read-only access to the internal app storage via the
+     * {@link OnDeviceIntelligenceService}. This is an asynchronous implementation for
+     * {@link #openFileInput(String)}.
+     *
+     * @param fileName       File name relative to the {@link Context#getFilesDir()}.
+     * @param resultConsumer Consumer to populate the corresponding file stream in.
+     */
+    public final void openFileInputAsync(@NonNull String fileName,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull Consumer<FileInputStream> resultConsumer) throws FileNotFoundException {
+        AndroidFuture<ParcelFileDescriptor> future = new AndroidFuture<>();
+        try {
+            mRemoteStorageService.getReadOnlyFileDescriptor(fileName, future);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Cannot open file due to remote service failure");
+            throw new FileNotFoundException(e.getMessage());
+        }
+        future.whenCompleteAsync((pfd, err) -> {
+            if (err != null) {
+                Log.e(TAG, "Failure when reading file: " + fileName + err);
+                executor.execute(() -> resultConsumer.accept(null));
+            } else {
+                executor.execute(
+                        () -> resultConsumer.accept(new FileInputStream(pfd.getFileDescriptor())));
+            }
+        }, executor);
+    }
+
+    /**
+     * Provides access to all file streams required for feature via the
+     * {@link OnDeviceIntelligenceService}.
+     *
+     * @param feature        Feature for which the associated files should be fetched.
+     * @param executor       Executor to run the consumer callback on.
+     * @param resultConsumer Consumer to receive a map of filePath to the corresponding file input
+     *                       stream.
+     */
+    public final void fetchFeatureFileInputStreamMap(@NonNull Feature feature,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull Consumer<Map<String, FileInputStream>> resultConsumer) {
+        try {
+            mRemoteStorageService.getReadOnlyFeatureFileDescriptorMap(feature,
+                    wrapResultReceiverAsReadOnly(resultConsumer, executor));
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private RemoteCallback wrapResultReceiverAsReadOnly(
+            @NonNull Consumer<Map<String, FileInputStream>> resultConsumer,
+            @NonNull Executor executor) {
+        return new RemoteCallback(result -> {
+            if (result == null) {
+                executor.execute(() -> resultConsumer.accept(new HashMap<>()));
+            } else {
+                Map<String, FileInputStream> bundleMap = new HashMap<>();
+                result.keySet().forEach(key -> {
+                    ParcelFileDescriptor pfd = result.getParcelable(key,
+                            ParcelFileDescriptor.class);
+                    if (pfd != null) {
+                        bundleMap.put(key, new FileInputStream(pfd.getFileDescriptor()));
+                    }
+                });
+                executor.execute(() -> resultConsumer.accept(bundleMap));
+            }
+        });
+    }
+
+    private OutcomeReceiver<Content,
+            OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException> wrapResponseCallback(
+            IResponseCallback callback) {
+        return new OutcomeReceiver<>() {
+            @Override
+            public void onResult(@androidx.annotation.NonNull Content response) {
+                try {
+                    callback.onSuccess(response);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Error sending result: " + e);
+                }
+            }
+
+            @Override
+            public void onError(
+                    OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException exception) {
+                try {
+                    callback.onFailure(exception.getErrorCode(), exception.getMessage(),
+                            exception.getErrorParams());
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Error sending result: " + e);
+                }
+            }
+        };
+    }
+
+    private StreamingResponseReceiver<Content, Content,
+            OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException> wrapStreamingResponseCallback(
+            IStreamingResponseCallback callback) {
+        return new StreamingResponseReceiver<>() {
+            @Override
+            public void onNewContent(@androidx.annotation.NonNull Content content) {
+                try {
+                    callback.onNewContent(content);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Error sending result: " + e);
+                }
+            }
+
+            @Override
+            public void onResult(@androidx.annotation.NonNull Content response) {
+                try {
+                    callback.onSuccess(response);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Error sending result: " + e);
+                }
+            }
+
+            @Override
+            public void onError(
+                    OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException exception) {
+                try {
+                    callback.onFailure(exception.getErrorCode(), exception.getMessage(),
+                            exception.getErrorParams());
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Error sending result: " + e);
+                }
+            }
+        };
+    }
+
+    private OutcomeReceiver<Long,
+            OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException> wrapTokenCountCallback(
+            ITokenCountCallback tokenCountCallback) {
+        return new OutcomeReceiver<>() {
+            @Override
+            public void onResult(Long tokenCount) {
+                try {
+                    tokenCountCallback.onSuccess(tokenCount);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Error sending result: " + e);
+                }
+            }
+
+            @Override
+            public void onError(
+                    OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException exception) {
+                try {
+                    tokenCountCallback.onFailure(exception.getErrorCode(), exception.getMessage(),
+                            exception.getErrorParams());
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Error sending failure: " + e);
+                }
+            }
+        };
+    }
+}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 8cbfdcb..cd6d79c 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -78,6 +78,7 @@
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.Intent;
+import android.content.IntentSender;
 import android.content.res.ColorStateList;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
@@ -5357,16 +5358,16 @@
     /**
      * Flag indicating that an unhandled drag should be delegated to the system to be started if no
      * visible window wishes to handle the drop. When using this flag, the caller must provide
-     * ClipData with an Item that contains an immutable PendingIntent to an activity to be launched
+     * ClipData with an Item that contains an immutable IntentSender to an activity to be launched
      * (not a broadcast, service, etc).  See
-     * {@link ClipData.Item.Builder#setPendingIntent(PendingIntent)}.
+     * {@link ClipData.Item.Builder#setIntentSender(IntentSender)}.
      *
      * The system can decide to launch the intent or not based on factors like the current screen
      * size or windowing mode. If the system does not launch the intent, it will be canceled via the
      * normal drag and drop flow.
      */
     @FlaggedApi(FLAG_DELEGATE_UNHANDLED_DRAGS)
-    public static final int DRAG_FLAG_START_PENDING_INTENT_ON_UNHANDLED_DRAG = 1 << 13;
+    public static final int DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG = 1 << 13;
 
     /**
      * Vertical scroll factor cached by {@link #getVerticalScrollFactor}.
@@ -28665,10 +28666,10 @@
             if (com.android.window.flags.Flags.delegateUnhandledDrags()) {
                 data.prepareToLeaveProcess(
                         (flags & (DRAG_FLAG_GLOBAL_SAME_APPLICATION | DRAG_FLAG_GLOBAL)) != 0);
-                if ((flags & DRAG_FLAG_START_PENDING_INTENT_ON_UNHANDLED_DRAG) != 0) {
-                    if (!data.hasActivityPendingIntents()) {
+                if ((flags & DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG) != 0) {
+                    if (!hasActivityPendingIntents(data)) {
                         // Reset the flag if there is no launchable activity intent
-                        flags &= ~DRAG_FLAG_START_PENDING_INTENT_ON_UNHANDLED_DRAG;
+                        flags &= ~DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG;
                         Log.w(VIEW_LOG_TAG, "startDragAndDrop called with "
                                 + "DRAG_FLAG_START_INTENT_ON_UNHANDLED_DRAG but the clip data "
                                 + "contains non-activity PendingIntents");
@@ -28781,7 +28782,7 @@
                     mAttachInfo.mDragSurface.release();
                 }
                 if (mAttachInfo.mDragData != null) {
-                    mAttachInfo.mDragData.cleanUpPendingIntents();
+                    View.cleanUpPendingIntents(mAttachInfo.mDragData);
                 }
                 mAttachInfo.mDragSurface = surface;
                 mAttachInfo.mDragToken = token;
@@ -28806,6 +28807,39 @@
         }
     }
 
+     /**
+     * Checks if this clip data has a pending intent that is an activity type.
+     * @hide
+     */
+    static boolean hasActivityPendingIntents(ClipData data) {
+        final int size = data.getItemCount();
+        for (int i = 0; i < size; i++) {
+            final ClipData.Item item = data.getItemAt(i);
+            if (item.getIntentSender() != null) {
+                final PendingIntent pi = new PendingIntent(item.getIntentSender().getTarget());
+                if (pi.isActivity()) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Cleans up all pending intents in the ClipData.
+     * @hide
+     */
+    static void cleanUpPendingIntents(ClipData data) {
+        final int size = data.getItemCount();
+        for (int i = 0; i < size; i++) {
+            final ClipData.Item item = data.getItemAt(i);
+            if (item.getIntentSender() != null) {
+                final PendingIntent pi = new PendingIntent(item.getIntentSender().getTarget());
+                pi.cancel();
+            }
+        }
+    }
+
     void setAccessibilityDragStarted(boolean started) {
         int pflags4 = mPrivateFlags4;
         if (started) {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 75deceb..0dee583 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -8626,7 +8626,7 @@
                         mAttachInfo.mDragSurface = null;
                     }
                     if (mAttachInfo.mDragData != null) {
-                        mAttachInfo.mDragData.cleanUpPendingIntents();
+                        View.cleanUpPendingIntents(mAttachInfo.mDragData);
                         mAttachInfo.mDragData = null;
                     }
                 }
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/android/window/flags/OWNERS b/core/java/android/window/flags/OWNERS
index fa81ee3..3fa3760 100644
--- a/core/java/android/window/flags/OWNERS
+++ b/core/java/android/window/flags/OWNERS
@@ -1 +1,2 @@
-per-file responsible_apis.aconfig = file:/BAL_OWNERS
\ No newline at end of file
+per-file responsible_apis.aconfig = file:/BAL_OWNERS
+per-file large_screen_experiences_app_compat.aconfig = file:/LSE_APP_COMPAT_OWNERS
diff --git a/core/java/com/android/internal/colorextraction/OWNERS b/core/java/com/android/internal/colorextraction/OWNERS
index ffade1e..041559c 100644
--- a/core/java/com/android/internal/colorextraction/OWNERS
+++ b/core/java/com/android/internal/colorextraction/OWNERS
@@ -1,3 +1,2 @@
-dupin@google.com
 cinek@google.com
-jamesoleary@google.com
+arteiro@google.com
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/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
index a7260bb..c34730f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
@@ -49,8 +49,13 @@
         this.mRemoteComposeState = remoteComposeState;
     }
 
-    public void reset() {
-        mBuffer.reset();
+    /**
+     * Reset the internal buffers
+     *
+     * @param expectedSize provided hint for the main buffer size
+     */
+    public void reset(int expectedSize) {
+        mBuffer.reset(expectedSize);
         mRemoteComposeState.reset();
     }
 
@@ -288,8 +293,7 @@
     public static void read(InputStream fd, RemoteComposeBuffer buffer) {
         try {
             byte[] bytes = readAllBytes(fd);
-            buffer.reset();
-            buffer.mBuffer.resize(bytes.length);
+            buffer.reset(bytes.length);
             System.arraycopy(bytes, 0, buffer.mBuffer.mBuffer, 0, bytes.length);
             buffer.mBuffer.mSize = bytes.length;
         } catch (Exception e) {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/WireBuffer.java b/core/java/com/android/internal/widget/remotecompose/core/WireBuffer.java
index 4518d94..b7cb392 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/WireBuffer.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/WireBuffer.java
@@ -83,10 +83,18 @@
         mIndex = currentIndex;
     }
 
-    public void reset() {
+    /**
+     * Reset the internal buffer
+     *
+     * @param expectedSize provided hint for the buffer size
+     */
+    public void reset(int expectedSize) {
         mIndex = 0;
         mStartingIndex = 0;
         mSize = 0;
+        if (expectedSize > mMaxSize) {
+            resize(expectedSize);
+        }
     }
 
     public int size() {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java
index 4bfdc59..76b7144 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java
@@ -33,7 +33,7 @@
     int mImageWidth;
     int mImageHeight;
     byte[] mBitmap;
-    public static final int MAX_IMAGE_DIMENSION = 6000;
+    public static final int MAX_IMAGE_DIMENSION = 8000;
 
     public static final Companion COMPANION = new Companion();
 
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index c6bc589..1f06b0b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4670,6 +4670,13 @@
    -->
     <string name="config_defaultWearableSensingService" translatable="false"></string>
 
+
+    <!-- The component name for the default system on-device intelligence service, -->
+    <string name="config_defaultOnDeviceIntelligenceService" translatable="false"></string>
+
+    <!-- The component name for the default system on-device trusted inference service. -->
+    <string name="config_defaultOnDeviceTrustedInferenceService" translatable="false"></string>
+
     <!-- Component name that accepts ACTION_SEND intents for requesting ambient context consent for
          wearable sensing. -->
     <string translatable="false" name="config_defaultWearableSensingConsentComponent"></string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 9d7acff..cf9c02a 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3909,6 +3909,8 @@
   <java-symbol type="string" name="config_ambientContextPackageNameExtraKey" />
   <java-symbol type="string" name="config_ambientContextEventArrayExtraKey" />
   <java-symbol type="string" name="config_defaultWearableSensingService" />
+  <java-symbol type="string" name="config_defaultOnDeviceIntelligenceService" />
+  <java-symbol type="string" name="config_defaultOnDeviceTrustedInferenceService" />
   <java-symbol type="string" name="config_retailDemoPackage" />
   <java-symbol type="string" name="config_retailDemoPackageSignature" />
 
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/BubblePositionerTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt
index 5825bbf..9cd14fca 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt
@@ -466,6 +466,26 @@
             .isEqualTo(expectedExpandedViewY)
     }
 
+    @Test
+    fun testGetTaskViewContentWidth_onLeft() {
+        positioner.update(defaultDeviceConfig.copy(insets = Insets.of(100, 0, 200, 0)))
+        val taskViewWidth = positioner.getTaskViewContentWidth(true /* onLeft */)
+        val paddings = positioner.getExpandedViewContainerPadding(true /* onLeft */,
+                false /* isOverflow */)
+        assertThat(taskViewWidth).isEqualTo(
+                positioner.screenRect.width() - paddings[0] - paddings[2])
+    }
+
+    @Test
+    fun testGetTaskViewContentWidth_onRight() {
+        positioner.update(defaultDeviceConfig.copy(insets = Insets.of(100, 0, 200, 0)))
+        val taskViewWidth = positioner.getTaskViewContentWidth(false /* onLeft */)
+        val paddings = positioner.getExpandedViewContainerPadding(false /* onLeft */,
+                false /* isOverflow */)
+        assertThat(taskViewWidth).isEqualTo(
+                positioner.screenRect.width() - paddings[0] - paddings[2])
+    }
+
     private val defaultYPosition: Float
         /**
          * Calculates the Y position bubbles should be placed based on the config. Based on the
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/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index cda29c9..a5853d6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -395,7 +395,7 @@
     public int getTaskViewContentWidth(boolean onLeft) {
         int[] paddings = getExpandedViewContainerPadding(onLeft, /* isOverflow = */ false);
         int pointerOffset = showBubblesVertically() ? getPointerSize() : 0;
-        return mPositionRect.width() - paddings[0] - paddings[2] - pointerOffset;
+        return mScreenRect.width() - paddings[0] - paddings[2] - pointerOffset;
     }
 
     /** Gets the y position of the expanded view if it was top-aligned. */
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/OWNERS b/packages/SettingsLib/Spa/OWNERS
index 464328e..67386d1 100644
--- a/packages/SettingsLib/Spa/OWNERS
+++ b/packages/SettingsLib/Spa/OWNERS
@@ -1,4 +1,4 @@
-set noparent
+include platform/frameworks/base:/packages/SettingsLib/OWNERS
 
 chaohuiw@google.com
 hanxu@google.com
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsOutlinedTextField.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsOutlinedTextField.kt
index 2ce3c66..bdc6a68 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsOutlinedTextField.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsOutlinedTextField.kt
@@ -19,6 +19,7 @@
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
 import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.OutlinedTextFieldDefaults
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
@@ -26,6 +27,7 @@
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.tooling.preview.Preview
 import com.android.settingslib.spa.framework.theme.SettingsDimension
 import com.android.settingslib.spa.framework.theme.SettingsTheme
@@ -37,7 +39,8 @@
     errorMessage: String? = null,
     singleLine: Boolean = true,
     enabled: Boolean = true,
-    onTextChange: (String) -> Unit,
+    shape: Shape = OutlinedTextFieldDefaults.shape,
+    onTextChange: (String) -> Unit
 ) {
     OutlinedTextField(
         modifier = Modifier
@@ -55,7 +58,8 @@
             if (errorMessage != null) {
                 Text(text = errorMessage)
             }
-        }
+        },
+        shape = shape
     )
 }
 
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/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index 581c7de..a376c1f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -92,13 +92,13 @@
         }
     }
 
-    protected String mPackageName;
+    @NonNull protected final String mPackageName;
     private MediaDevice mCurrentConnectedDevice;
     private final LocalBluetoothManager mBluetoothManager;
     private final Map<String, RouteListingPreference.Item> mPreferenceItemMap =
             new ConcurrentHashMap<>();
 
-    public InfoMediaManager(
+    /* package */ InfoMediaManager(
             Context context,
             @NonNull String packageName,
             Notification notification,
@@ -112,7 +112,7 @@
     /** Creates an instance of InfoMediaManager. */
     public static InfoMediaManager createInstance(
             Context context,
-            String packageName,
+            @Nullable String packageName,
             Notification notification,
             LocalBluetoothManager localBluetoothManager) {
 
@@ -148,8 +148,7 @@
     }
 
     private void updateRouteListingPreference() {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE
-                && !TextUtils.isEmpty(mPackageName)) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
             RouteListingPreference routeListingPreference =
                     getRouteListingPreference();
             Api34Impl.onRouteListingPreferenceUpdated(routeListingPreference,
@@ -218,11 +217,7 @@
     protected final void rebuildDeviceList() {
         mMediaDevices.clear();
         mCurrentConnectedDevice = null;
-        if (TextUtils.isEmpty(mPackageName)) {
-            buildAllRoutes();
-        } else {
-            buildAvailableRoutes();
-        }
+        buildAvailableRoutes();
     }
 
     protected final void notifyCurrentConnectedDeviceChanged() {
@@ -250,12 +245,8 @@
             return;
         }
 
-        if (TextUtils.isEmpty(mPackageName)) {
-            connectDeviceWithoutPackageName(device);
-        } else {
-            device.setConnectedRecord();
-            transferToRoute(device.mRouteInfo);
-        }
+        device.setConnectedRecord();
+        transferToRoute(device.mRouteInfo);
     }
 
     /**
@@ -265,11 +256,6 @@
      * @return If add device successful return {@code true}, otherwise return {@code false}
      */
     boolean addDeviceToPlayMedia(MediaDevice device) {
-        if (TextUtils.isEmpty(mPackageName)) {
-            Log.w(TAG, "addDeviceToPlayMedia() package name is null or empty!");
-            return false;
-        }
-
         final RoutingSessionInfo info = getRoutingSessionInfo();
         if (info == null || !info.getSelectableRoutes().contains(device.mRouteInfo.getId())) {
             Log.w(TAG, "addDeviceToPlayMedia() Ignoring selecting a non-selectable device : "
@@ -306,7 +292,6 @@
 
     boolean preferRouteListingOrdering() {
         return Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE
-                && !TextUtils.isEmpty(mPackageName)
                 && Api34Impl.preferRouteListingOrdering(getRouteListingPreference());
     }
 
@@ -326,11 +311,6 @@
      * @return If device stop successful return {@code true}, otherwise return {@code false}
      */
     boolean removeDeviceFromPlayMedia(MediaDevice device) {
-        if (TextUtils.isEmpty(mPackageName)) {
-            Log.w(TAG, "removeDeviceFromMedia() package name is null or empty!");
-            return false;
-        }
-
         final RoutingSessionInfo info = getRoutingSessionInfo();
         if (info == null || !info.getSelectedRoutes().contains(device.mRouteInfo.getId())) {
             Log.w(TAG, "removeDeviceFromMedia() Ignoring deselecting a non-deselectable device : "
@@ -346,11 +326,6 @@
      * Release session to stop playing media on MediaDevice.
      */
     boolean releaseSession() {
-        if (TextUtils.isEmpty(mPackageName)) {
-            Log.w(TAG, "releaseSession() package name is null or empty!");
-            return false;
-        }
-
         final RoutingSessionInfo sessionInfo = getRoutingSessionInfo();
         if (sessionInfo == null) {
             Log.w(TAG, "releaseSession() Ignoring release session : " + mPackageName);
@@ -367,11 +342,6 @@
      */
     @NonNull
     List<MediaDevice> getSelectableMediaDevices() {
-        if (TextUtils.isEmpty(mPackageName)) {
-            Log.w(TAG, "getSelectableMediaDevices() package name is null or empty!");
-            return Collections.emptyList();
-        }
-
         final RoutingSessionInfo info = getRoutingSessionInfo();
         if (info == null) {
             Log.w(TAG, "getSelectableMediaDevices() cannot find selectable MediaDevice from : "
@@ -394,11 +364,6 @@
      */
     @NonNull
     List<MediaDevice> getDeselectableMediaDevices() {
-        if (TextUtils.isEmpty(mPackageName)) {
-            Log.d(TAG, "getDeselectableMediaDevices() package name is null or empty!");
-            return Collections.emptyList();
-        }
-
         final RoutingSessionInfo info = getRoutingSessionInfo();
         if (info == null) {
             Log.d(TAG, "getDeselectableMediaDevices() cannot find deselectable MediaDevice from : "
@@ -462,11 +427,6 @@
      * @param volume the value of volume
      */
     void adjustSessionVolume(int volume) {
-        if (TextUtils.isEmpty(mPackageName)) {
-            Log.w(TAG, "adjustSessionVolume() package name is null or empty!");
-            return;
-        }
-
         final RoutingSessionInfo info = getRoutingSessionInfo();
         if (info == null) {
             Log.w(TAG, "adjustSessionVolume() can't found corresponding RoutingSession with : "
@@ -484,11 +444,6 @@
      * @return  maximum volume of the session, and return -1 if not found.
      */
     public int getSessionVolumeMax() {
-        if (TextUtils.isEmpty(mPackageName)) {
-            Log.w(TAG, "getSessionVolumeMax() package name is null or empty!");
-            return -1;
-        }
-
         final RoutingSessionInfo info = getRoutingSessionInfo();
         if (info == null) {
             Log.w(TAG, "getSessionVolumeMax() can't find corresponding RoutingSession with : "
@@ -505,11 +460,6 @@
      * @return current volume of the session, and return -1 if not found.
      */
     public int getSessionVolume() {
-        if (TextUtils.isEmpty(mPackageName)) {
-            Log.w(TAG, "getSessionVolume() package name is null or empty!");
-            return -1;
-        }
-
         final RoutingSessionInfo info = getRoutingSessionInfo();
         if (info == null) {
             Log.w(TAG, "getSessionVolume() can't find corresponding RoutingSession with : "
@@ -521,11 +471,6 @@
     }
 
     CharSequence getSessionName() {
-        if (TextUtils.isEmpty(mPackageName)) {
-            Log.w(TAG, "Unable to get session name. The package name is null or empty!");
-            return null;
-        }
-
         final RoutingSessionInfo info = getRoutingSessionInfo();
         if (info == null) {
             Log.w(TAG, "Unable to get session name for package: " + mPackageName);
@@ -548,20 +493,6 @@
 
     // MediaRoute2Info.getType was made public on API 34, but exists since API 30.
     @SuppressWarnings("NewApi")
-    private void buildAllRoutes() {
-        for (MediaRoute2Info route : getAllRoutes()) {
-            if (DEBUG) {
-                Log.d(TAG, "buildAllRoutes() route : " + route.getName() + ", volume : "
-                        + route.getVolume() + ", type : " + route.getType());
-            }
-            if (route.isSystemRoute()) {
-                addMediaDevice(route);
-            }
-        }
-    }
-
-    // MediaRoute2Info.getType was made public on API 34, but exists since API 30.
-    @SuppressWarnings("NewApi")
     private synchronized void buildAvailableRoutes() {
         for (MediaRoute2Info route : getAvailableRoutes()) {
             if (DEBUG) {
@@ -594,8 +525,7 @@
                 infos.add(transferableRoute);
             }
         }
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE
-                && !TextUtils.isEmpty(mPackageName)) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
             RouteListingPreference routeListingPreference = getRouteListingPreference();
             if (routeListingPreference != null) {
                 final List<RouteListingPreference.Item> preferenceRouteListing =
@@ -679,7 +609,7 @@
                 break;
         }
 
-        if (mediaDevice != null && !TextUtils.isEmpty(mPackageName)
+        if (mediaDevice != null
                 && getRoutingSessionInfo().getSelectedRoutes().contains(route.getId())) {
             mediaDevice.setState(STATE_SELECTED);
             if (mCurrentConnectedDevice == null) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java
index 97bbf12..cf8906d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java
@@ -51,9 +51,9 @@
 
     private final Executor mExecutor = Executors.newSingleThreadExecutor();
 
-    public ManagerInfoMediaManager(
+    /* package */ ManagerInfoMediaManager(
             Context context,
-            String packageName,
+            @NonNull String packageName,
             Notification notification,
             LocalBluetoothManager localBluetoothManager) {
         super(context, packageName, notification, localBluetoothManager);
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java
index 9d578bc..5b1c8ef 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java
@@ -41,7 +41,7 @@
 
     NoOpInfoMediaManager(
             Context context,
-            String packageName,
+            @NonNull String packageName,
             Notification notification,
             LocalBluetoothManager localBluetoothManager) {
         super(context, packageName, notification, localBluetoothManager);
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java
index aef09ac..c8c8b672 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java
@@ -65,9 +65,9 @@
             };
 
     // TODO (b/321969740): Plumb target UserHandle between UMO and RouterInfoMediaManager.
-    public RouterInfoMediaManager(
+    /* package */ RouterInfoMediaManager(
             Context context,
-            String packageName,
+            @NonNull String packageName,
             Notification notification,
             LocalBluetoothManager localBluetoothManager)
             throws PackageNotAvailableException {
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/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index f0330c4..290e63ca 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -212,28 +212,6 @@
     }
 
     @Test
-    public void onRouteAdded_buildAllRoutes_shouldAddMediaDevice() {
-        final MediaRoute2Info info = mock(MediaRoute2Info.class);
-        when(info.getId()).thenReturn(TEST_ID);
-        when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
-        when(info.isSystemRoute()).thenReturn(true);
-
-        final List<MediaRoute2Info> routes = new ArrayList<>();
-        routes.add(info);
-        mShadowRouter2Manager.setAllRoutes(routes);
-
-        final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
-        assertThat(mediaDevice).isNull();
-
-        mInfoMediaManager.mPackageName = "";
-        mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated();
-
-        final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
-        assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
-        assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size());
-    }
-
-    @Test
     public void onPreferredFeaturesChanged_samePackageName_shouldAddMediaDevice() {
         final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
         final RoutingSessionInfo sessionInfo = mock(RoutingSessionInfo.class);
@@ -436,29 +414,6 @@
     }
 
     @Test
-    public void onRoutesChanged_buildAllRoutes_shouldAddMediaDevice() {
-        final MediaRoute2Info info = mock(MediaRoute2Info.class);
-        when(info.getId()).thenReturn(TEST_ID);
-        when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
-        when(info.isSystemRoute()).thenReturn(true);
-        when(info.getDeduplicationIds()).thenReturn(Set.of());
-
-        final List<MediaRoute2Info> routes = new ArrayList<>();
-        routes.add(info);
-        mShadowRouter2Manager.setAllRoutes(routes);
-
-        final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
-        assertThat(mediaDevice).isNull();
-
-        mInfoMediaManager.mPackageName = "";
-        mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated();
-
-        final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
-        assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
-        assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size());
-    }
-
-    @Test
     public void hasPreferenceRouteListing_oldSdkVersion_returnsFalse() {
         assertThat(mInfoMediaManager.preferRouteListingOrdering()).isFalse();
     }
@@ -587,36 +542,6 @@
     }
 
     @Test
-    public void onRoutesRemoved_buildAllRoutes_shouldAddMediaDevice() {
-        final MediaRoute2Info info = mock(MediaRoute2Info.class);
-        when(info.getId()).thenReturn(TEST_ID);
-        when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
-        when(info.isSystemRoute()).thenReturn(true);
-
-        final List<MediaRoute2Info> routes = new ArrayList<>();
-        routes.add(info);
-        when(mRouterManager.getAllRoutes()).thenReturn(routes);
-
-        final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
-        assertThat(mediaDevice).isNull();
-
-        mInfoMediaManager.mPackageName = "";
-        mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated();
-
-        final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
-        assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
-        assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size());
-    }
-
-    @Test
-    public void addDeviceToPlayMedia_packageNameIsNull_returnFalse() {
-        mInfoMediaManager.mPackageName = null;
-        final MediaDevice device = mock(MediaDevice.class);
-
-        assertThat(mInfoMediaManager.addDeviceToPlayMedia(device)).isFalse();
-    }
-
-    @Test
     public void addDeviceToPlayMedia_containSelectableRoutes_returnTrue() {
         final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
         final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
@@ -660,14 +585,6 @@
     }
 
     @Test
-    public void removeDeviceFromMedia_packageNameIsNull_returnFalse() {
-        mInfoMediaManager.mPackageName = null;
-        final MediaDevice device = mock(MediaDevice.class);
-
-        assertThat(mInfoMediaManager.removeDeviceFromPlayMedia(device)).isFalse();
-    }
-
-    @Test
     public void removeDeviceFromMedia_containSelectedRoutes_returnTrue() {
         final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
         final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
@@ -711,13 +628,6 @@
     }
 
     @Test
-    public void getSelectableMediaDevice_packageNameIsNull_returnFalse() {
-        mInfoMediaManager.mPackageName = null;
-
-        assertThat(mInfoMediaManager.getSelectableMediaDevices()).isEmpty();
-    }
-
-    @Test
     public void getSelectableMediaDevice_notContainPackageName_returnEmpty() {
         final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
         final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
@@ -730,13 +640,6 @@
     }
 
     @Test
-    public void getDeselectableMediaDevice_packageNameIsNull_returnFalse() {
-        mInfoMediaManager.mPackageName = null;
-
-        assertThat(mInfoMediaManager.getDeselectableMediaDevices()).isEmpty();
-    }
-
-    @Test
     public void getDeselectableMediaDevice_checkList() {
         final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
         final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
@@ -761,20 +664,6 @@
     }
 
     @Test
-    public void adjustSessionVolume_packageNameIsNull_noCrash() {
-        mInfoMediaManager.mPackageName = null;
-
-        mInfoMediaManager.adjustSessionVolume(10);
-    }
-
-    @Test
-    public void getSessionVolumeMax_packageNameIsNull_returnNotFound() {
-        mInfoMediaManager.mPackageName = null;
-
-        assertThat(mInfoMediaManager.getSessionVolumeMax()).isEqualTo(-1);
-    }
-
-    @Test
     public void getSessionVolumeMax_containPackageName_returnMaxVolume() {
         final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
         final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
@@ -800,13 +689,6 @@
     }
 
     @Test
-    public void getSessionVolume_packageNameIsNull_returnNotFound() {
-        mInfoMediaManager.mPackageName = null;
-
-        assertThat(mInfoMediaManager.getSessionVolume()).isEqualTo(-1);
-    }
-
-    @Test
     public void getSessionVolume_containPackageName_returnMaxVolume() {
         final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
         final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
@@ -841,13 +723,6 @@
     }
 
     @Test
-    public void releaseSession_packageNameIsNull_returnFalse() {
-        mInfoMediaManager.mPackageName = null;
-
-        assertThat(mInfoMediaManager.releaseSession()).isFalse();
-    }
-
-    @Test
     public void releaseSession_removeSuccessfully_returnTrue() {
         final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
         final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
@@ -860,13 +735,6 @@
     }
 
     @Test
-    public void getSessionName_packageNameIsNull_returnNull() {
-        mInfoMediaManager.mPackageName = null;
-
-        assertThat(mInfoMediaManager.getSessionName()).isNull();
-    }
-
-    @Test
     public void getSessionName_routeSessionInfoIsNull_returnNull() {
         final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
         final RoutingSessionInfo info = null;
@@ -942,32 +810,6 @@
     }
 
     @Test
-    public void onTransferred_buildAllRoutes_shouldAddMediaDevice() {
-        final MediaRoute2Info info = mock(MediaRoute2Info.class);
-        final RoutingSessionInfo sessionInfo = mock(RoutingSessionInfo.class);
-        mInfoMediaManager.registerCallback(mCallback);
-
-        when(info.getId()).thenReturn(TEST_ID);
-        when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
-        when(info.isSystemRoute()).thenReturn(true);
-
-        final List<MediaRoute2Info> routes = new ArrayList<>();
-        routes.add(info);
-        mShadowRouter2Manager.setAllRoutes(routes);
-
-        final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
-        assertThat(mediaDevice).isNull();
-
-        mInfoMediaManager.mPackageName = "";
-        mInfoMediaManager.mMediaRouterCallback.onTransferred(sessionInfo, sessionInfo);
-
-        final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
-        assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
-        assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size());
-        verify(mCallback).onConnectedDeviceChanged(null);
-    }
-
-    @Test
     public void onSessionUpdated_shouldDispatchDeviceListAdded() {
         final MediaRoute2Info info = mock(MediaRoute2Info.class);
         when(info.getId()).thenReturn(TEST_ID);
@@ -978,7 +820,6 @@
         routes.add(info);
         mShadowRouter2Manager.setAllRoutes(routes);
 
-        mInfoMediaManager.mPackageName = "";
         mInfoMediaManager.registerCallback(mCallback);
 
         mInfoMediaManager.mMediaRouterCallback.onSessionUpdated(mock(RoutingSessionInfo.class));
diff --git a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowRouter2Manager.java b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowRouter2Manager.java
index fde378f..3adb204 100644
--- a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowRouter2Manager.java
+++ b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowRouter2Manager.java
@@ -27,12 +27,13 @@
 import org.robolectric.annotation.Implements;
 import org.robolectric.shadow.api.Shadow;
 
+import java.util.ArrayList;
 import java.util.List;
 
 @Implements(MediaRouter2Manager.class)
 public class ShadowRouter2Manager {
 
-    private List<MediaRoute2Info> mAvailableRoutes;
+    private List<MediaRoute2Info> mAvailableRoutes = new ArrayList<>();
     private List<MediaRoute2Info> mAllRoutes;
     private List<MediaRoute2Info> mDeselectableRoutes;
     private List<RoutingSessionInfo> mRemoteSessions;
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 65cea69..4ed1965 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -451,6 +451,16 @@
 }
 
 flag {
+    name: "slice_manager_binder_call_background"
+    namespace: "systemui"
+    description: "Move the ISliceManager#getPinnedSpecs binder call to the background thread."
+    bug: "322745650"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
    name: "register_new_wallet_card_in_background"
    namespace: "systemui"
    description: "Decide whether the call to registerNewWalletCards method should be issued on background thread."
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/domain/interactor/KeyguardTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
index 9368097..3484025 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
@@ -197,7 +197,16 @@
             runCurrent()
         }
 
-        assertThat(startedSteps).isEqualTo(listOf(steps[0], steps[3], steps[6]))
+        assertThat(startedSteps)
+            .isEqualTo(
+                listOf(
+                    // The initial transition will also get sent when collect started
+                    TransitionStep(OFF, LOCKSCREEN, 0f, STARTED),
+                    steps[0],
+                    steps[3],
+                    steps[6]
+                )
+            )
     }
 
     @Test
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/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
index 15cf83c..47e1ee9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
@@ -119,15 +119,19 @@
     fun lockscreenAlpha_runDimissFromKeyguard() =
         testScope.runTest {
             val values by collectValues(underTest.lockscreenAlpha)
+            sysuiStatusBarStateController.setLeaveOpenOnKeyguardHide(true)
             runCurrent()
 
-            sysuiStatusBarStateController.setLeaveOpenOnKeyguardHide(true)
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.PRIMARY_BOUNCER,
+                to = KeyguardState.GONE,
+                testScope,
+            )
 
-            keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED))
-            keyguardTransitionRepository.sendTransitionStep(step(1f))
-
-            assertThat(values.size).isEqualTo(2)
-            values.forEach { assertThat(it).isEqualTo(1f) }
+            assertThat(values[0]).isEqualTo(1f)
+            assertThat(values[1]).isEqualTo(1f)
+            // Ensure FINISHED sets alpha to 0
+            assertThat(values[2]).isEqualTo(0f)
         }
 
     @Test
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/proguard_common.flags b/packages/SystemUI/proguard_common.flags
index f9546c4..162d8ae 100644
--- a/packages/SystemUI/proguard_common.flags
+++ b/packages/SystemUI/proguard_common.flags
@@ -17,10 +17,6 @@
   <1> *;
 }
 
--keepclasseswithmembers class * {
-    public <init>(android.content.Context, android.util.AttributeSet);
-}
-
 -keep class androidx.core.app.CoreComponentFactory
 
 # Keep the wm shell lib
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
index a9928d8..63088aa 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
@@ -20,6 +20,7 @@
 
 import android.app.PendingIntent;
 import android.net.Uri;
+import android.os.Handler;
 import android.os.Trace;
 import android.provider.Settings;
 import android.util.Log;
@@ -39,6 +40,9 @@
 
 import com.android.keyguard.dagger.KeyguardStatusViewScope;
 import com.android.systemui.Dumpable;
+import com.android.systemui.Flags;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.KeyguardSliceProvider;
 import com.android.systemui.plugins.ActivityStarter;
@@ -60,6 +64,8 @@
         Dumpable {
     private static final String TAG = "KeyguardSliceViewCtrl";
 
+    private final Handler mHandler;
+    private final Handler mBgHandler;
     private final ActivityStarter mActivityStarter;
     private final ConfigurationController mConfigurationController;
     private final TunerService mTunerService;
@@ -105,6 +111,8 @@
 
     @Inject
     public KeyguardSliceViewController(
+            @Main Handler handler,
+            @Background Handler bgHandler,
             KeyguardSliceView keyguardSliceView,
             ActivityStarter activityStarter,
             ConfigurationController configurationController,
@@ -112,6 +120,8 @@
             DumpManager dumpManager,
             DisplayTracker displayTracker) {
         super(keyguardSliceView);
+        mHandler = handler;
+        mBgHandler = bgHandler;
         mActivityStarter = activityStarter;
         mConfigurationController = configurationController;
         mTunerService = tunerService;
@@ -182,24 +192,34 @@
      * Update contents of the view.
      */
     public void refresh() {
-        Slice slice;
+
         Trace.beginSection("KeyguardSliceViewController#refresh");
-        // We can optimize performance and avoid binder calls when we know that we're bound
-        // to a Slice on the same process.
-        if (KeyguardSliceProvider.KEYGUARD_SLICE_URI.equals(mKeyguardSliceUri.toString())) {
-            KeyguardSliceProvider instance = KeyguardSliceProvider.getAttachedInstance();
-            if (instance != null) {
-                slice = instance.onBindSlice(mKeyguardSliceUri);
+        try {
+            Slice slice;
+            if (KeyguardSliceProvider.KEYGUARD_SLICE_URI.equals(mKeyguardSliceUri.toString())) {
+                KeyguardSliceProvider instance = KeyguardSliceProvider.getAttachedInstance();
+                if (instance != null) {
+                    if (Flags.sliceManagerBinderCallBackground()) {
+                        mBgHandler.post(() -> {
+                            Slice _slice = instance.onBindSlice(mKeyguardSliceUri);
+                            mHandler.post(() -> mObserver.onChanged(_slice));
+                        });
+                        return;
+                    }
+                    slice = instance.onBindSlice(mKeyguardSliceUri);
+                } else {
+                    Log.w(TAG, "Keyguard slice not bound yet?");
+                    slice = null;
+                }
             } else {
-                Log.w(TAG, "Keyguard slice not bound yet?");
-                slice = null;
+                // TODO: Make SliceViewManager injectable
+                slice = SliceViewManager.getInstance(mView.getContext()).bindSlice(
+                        mKeyguardSliceUri);
             }
-        } else {
-            // TODO: Make SliceViewManager injectable
-            slice = SliceViewManager.getInstance(mView.getContext()).bindSlice(mKeyguardSliceUri);
+            mObserver.onChanged(slice);
+        } finally {
+            Trace.endSection();
         }
-        mObserver.onChanged(slice);
-        Trace.endSection();
     }
 
     void showSlice(Slice slice) {
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/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt
index 188be24..4db942cc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt
@@ -20,7 +20,9 @@
 import androidx.annotation.VisibleForTesting
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordanceModel
+import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
 import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
@@ -57,6 +59,7 @@
     lockscreenToGoneTransitionViewModel: LockscreenToGoneTransitionViewModel,
     lockscreenToOccludedTransitionViewModel: LockscreenToOccludedTransitionViewModel,
     lockscreenToPrimaryBouncerTransitionViewModel: LockscreenToPrimaryBouncerTransitionViewModel,
+    transitionInteractor: KeyguardTransitionInteractor,
 ) {
 
     data class PreviewMode(
@@ -71,6 +74,24 @@
      */
     private val previewMode = MutableStateFlow(PreviewMode())
 
+    private val showingLockscreen: Flow<Boolean> =
+        transitionInteractor.finishedKeyguardState.map { keyguardState ->
+            keyguardState == KeyguardState.LOCKSCREEN
+        }
+
+    /** The only time the expansion is important is while lockscreen is actively displayed */
+    private val shadeExpansionAlpha =
+        combine(
+            showingLockscreen,
+            shadeInteractor.anyExpansion,
+        ) { showingLockscreen, expansion ->
+            if (showingLockscreen) {
+                1 - expansion
+            } else {
+                0f
+            }
+        }
+
     /**
      * ID of the slot that's currently selected in the preview that renders exclusively in the
      * wallpaper picker application. This is ignored for the actual, real lock screen experience.
@@ -101,7 +122,7 @@
             lockscreenToGoneTransitionViewModel.shortcutsAlpha,
             lockscreenToOccludedTransitionViewModel.shortcutsAlpha,
             lockscreenToPrimaryBouncerTransitionViewModel.shortcutsAlpha,
-            shadeInteractor.qsExpansion.map { 1 - it },
+            shadeExpansionAlpha,
         )
 
     /** The source of truth of alpha for all of the quick affordances on lockscreen */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
index 4f28b46..378ce52 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
@@ -108,6 +108,7 @@
                     0f
                 }
             },
+            onFinish = { 0f },
         )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt
index e15e038..c629337 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt
@@ -230,18 +230,12 @@
         val currentAllowMediaPlayerOnLockScreen = allowMediaPlayerOnLockScreen
         val useSplitShade = useSplitShade
         val shouldBeVisibleForSplitShade = shouldBeVisibleForSplitShade()
-
         visible =
             isMediaHostVisible &&
                 isBypassNotEnabled &&
                 keyguardOrUserSwitcher &&
                 currentAllowMediaPlayerOnLockScreen &&
                 shouldBeVisibleForSplitShade
-        if (visible) {
-            showMediaPlayer()
-        } else {
-            hideMediaPlayer()
-        }
         logger.logRefreshMediaPosition(
             reason = reason,
             visible = visible,
@@ -251,8 +245,17 @@
             mediaHostVisible = isMediaHostVisible,
             bypassNotEnabled = isBypassNotEnabled,
             currentAllowMediaPlayerOnLockScreen = currentAllowMediaPlayerOnLockScreen,
-            shouldBeVisibleForSplitShade = shouldBeVisibleForSplitShade
+            shouldBeVisibleForSplitShade = shouldBeVisibleForSplitShade,
         )
+        val currActiveContainer = activeContainer
+
+        logger.logActiveMediaContainer("before refreshMediaPosition", currActiveContainer)
+        if (visible) {
+            showMediaPlayer()
+        } else {
+            hideMediaPlayer()
+        }
+        logger.logActiveMediaContainer("after refreshMediaPosition", currActiveContainer)
 
         lastUsedStatusBarState = currentState
     }
@@ -293,9 +296,11 @@
     }
 
     private fun setVisibility(view: ViewGroup?, newVisibility: Int) {
-        val previousVisibility = view?.visibility
-        view?.visibility = newVisibility
-        if (previousVisibility != newVisibility) {
+        val currentMediaContainer = view ?: return
+
+        val previousVisibility = currentMediaContainer.visibility
+        currentMediaContainer.visibility = newVisibility
+        if (previousVisibility != newVisibility && currentMediaContainer is MediaContainerView) {
             visibilityChangedListener?.invoke(newVisibility == View.VISIBLE)
         }
     }
@@ -325,4 +330,7 @@
             }
         }
     }
+
+    private val activeContainer: ViewGroup? =
+        if (useSplitShade) splitShadeContainer else singlePaneContainer
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerLogger.kt
index 41fef88..0dd4b58 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerLogger.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.media.controls.ui
 
+import android.view.ViewGroup
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.core.LogLevel.DEBUG
 import com.android.systemui.log.dagger.KeyguardMediaControllerLog
@@ -36,8 +37,8 @@
         mediaHostVisible: Boolean,
         bypassNotEnabled: Boolean,
         currentAllowMediaPlayerOnLockScreen: Boolean,
-        shouldBeVisibleForSplitShade: Boolean
-    ) =
+        shouldBeVisibleForSplitShade: Boolean,
+    ) {
         logBuffer.log(
             TAG,
             DEBUG,
@@ -63,6 +64,19 @@
                     "shouldBeVisibleForSplitShade=$str3)"
             }
         )
+    }
+
+    fun logActiveMediaContainer(reason: String, activeContainer: ViewGroup?) {
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                str1 = reason
+                str2 = activeContainer.toString()
+            },
+            { "activeMediaContainerVisibility(reason=$str1, activeContainer=$str2)" }
+        )
+    }
 
     private companion object {
         private const val TAG = "KeyguardMediaControllerLog"
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index 99e91c1..e577178 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -511,6 +511,12 @@
                             return true;
                         }
                     }
+                } else if (migrateClocksToBlueprint()) {
+                    // This final check handles swipes on HUNs and when Pulsing
+                    if (!bouncerShowing && didNotificationPanelInterceptEvent(ev)) {
+                        mShadeLogger.d("NSWVC: intercepted for HUN/PULSING");
+                        return true;
+                    }
                 }
                 return false;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index ca19f71..3908ede 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -615,14 +615,7 @@
             args.argi2 = state1;
             args.argi3 = state2;
             args.argi4 = animate ? 1 : 0;
-            Message msg = mHandler.obtainMessage(MSG_DISABLE, args);
-            if (Looper.myLooper() == mHandler.getLooper()) {
-                // If its the right looper execute immediately so hides can be handled quickly.
-                mHandler.handleMessage(msg);
-                msg.recycle();
-            } else {
-                msg.sendToTarget();
-            }
+            mHandler.obtainMessage(MSG_DISABLE, args).sendToTarget();
         }
     }
 
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/data/repository/StatusBarModePerDisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt
index 6429815..11636bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt
@@ -17,6 +17,8 @@
 package com.android.systemui.statusbar.data.repository
 
 import android.graphics.Rect
+import android.view.InsetsFlags
+import android.view.ViewDebug
 import android.view.WindowInsets
 import android.view.WindowInsetsController
 import android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS
@@ -305,8 +307,8 @@
         letterboxDetails.isNotEmpty()
 
     override fun dump(pw: PrintWriter, args: Array<out String>) {
-        pw.println("originalStatusBarAttributes: ${_originalStatusBarAttributes.value}")
-        pw.println("modifiedStatusBarAttributes: ${modifiedStatusBarAttributes.value}")
+        pw.println("${_originalStatusBarAttributes.value}")
+        pw.println("${modifiedStatusBarAttributes.value}")
         pw.println("statusBarMode: ${statusBarMode.value}")
     }
 
@@ -320,7 +322,20 @@
         val navbarColorManagedByIme: Boolean,
         @WindowInsets.Type.InsetsType val requestedVisibleTypes: Int,
         val letterboxDetails: List<LetterboxDetails>,
-    )
+    ) {
+        override fun toString(): String {
+            return """
+                StatusBarAttributes(
+                    appearance=${appearance.toAppearanceString()},
+                    appearanceRegions=$appearanceRegions,
+                    navbarColorManagedByIme=$navbarColorManagedByIme,
+                    requestedVisibleTypes=${requestedVisibleTypes.toWindowInsetsString()},
+                    letterboxDetails=$letterboxDetails
+                    )
+                    """
+                .trimIndent()
+        }
+    }
 
     /**
      * Internal class keeping track of how [StatusBarAttributes] were transformed into new
@@ -331,9 +346,31 @@
         val appearanceRegions: List<AppearanceRegion>,
         val navbarColorManagedByIme: Boolean,
         val statusBarBounds: BoundsPair,
-    )
+    ) {
+        override fun toString(): String {
+            return """
+                ModifiedStatusBarAttributes(
+                    appearance=${appearance.toAppearanceString()},
+                    appearanceRegions=$appearanceRegions,
+                    navbarColorManagedByIme=$navbarColorManagedByIme,
+                    statusBarBounds=$statusBarBounds
+                    )
+                    """
+                .trimIndent()
+        }
+    }
 }
 
+private fun @receiver:WindowInsets.Type.InsetsType Int.toWindowInsetsString() =
+    "[${WindowInsets.Type.toString(this).replace(" ", ", ")}]"
+
+private fun @receiver:Appearance Int.toAppearanceString() =
+    if (this == 0) {
+        "NONE"
+    } else {
+        ViewDebug.flagsToString(InsetsFlags::class.java, "appearance", this)
+    }
+
 @AssistedFactory
 interface StatusBarModePerDisplayRepositoryFactory {
     fun create(@Assisted("displayId") displayId: Int): StatusBarModePerDisplayRepositoryImpl
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/keyguard/KeyguardSliceViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java
index 4d3243a..edb910a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java
@@ -22,8 +22,10 @@
 import static org.mockito.Mockito.when;
 
 import android.content.pm.PackageManager;
+import android.os.Handler;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 import android.view.View;
 
@@ -57,17 +59,22 @@
     private ActivityStarter mActivityStarter;
     private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
     private DumpManager mDumpManager = new DumpManager();
-
+    private Handler mHandler;
+    private Handler mBgHandler;
     private KeyguardSliceViewController mController;
 
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
+        TestableLooper testableLooper = TestableLooper.get(this);
+        assert testableLooper != null;
+        mHandler = new Handler(testableLooper.getLooper());
+        mBgHandler = new Handler(testableLooper.getLooper());
         when(mView.isAttachedToWindow()).thenReturn(true);
         when(mView.getContext()).thenReturn(mContext);
-        mController = new KeyguardSliceViewController(
-                mView, mActivityStarter, mConfigurationController,
-                mTunerService, mDumpManager, mDisplayTracker);
+        mController = new KeyguardSliceViewController(mHandler, mBgHandler, mView,
+                mActivityStarter, mConfigurationController, mTunerService, mDumpManager,
+                mDisplayTracker);
         mController.setupUri(KeyguardSliceProvider.KEYGUARD_SLICE_URI);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
index 18a34ba..1f14afa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
@@ -44,6 +44,8 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
 import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
 import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
 import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger
@@ -75,6 +77,7 @@
 import org.mockito.Mock
 import org.mockito.Mockito
 import org.mockito.MockitoAnnotations
+import kotlin.test.assertEquals
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
@@ -130,6 +133,8 @@
     @Mock
     private lateinit var lockscreenToPrimaryBouncerTransitionViewModel:
         LockscreenToPrimaryBouncerTransitionViewModel
+    @Mock
+    private lateinit var transitionInteractor: KeyguardTransitionInteractor
 
     private lateinit var underTest: KeyguardQuickAffordancesCombinedViewModel
 
@@ -146,6 +151,8 @@
     // the viewModel does a `map { 1 - it }` on this value, which is why it's different
     private val intendedShadeAlphaMutableStateFlow: MutableStateFlow<Float> = MutableStateFlow(0f)
 
+    private val intendedFinishedKeyguardStateFlow = MutableStateFlow(KeyguardState.LOCKSCREEN)
+
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
@@ -242,6 +249,7 @@
 
         intendedAlphaMutableStateFlow.value = 1f
         intendedShadeAlphaMutableStateFlow.value = 0f
+        intendedFinishedKeyguardStateFlow.value = KeyguardState.LOCKSCREEN
         whenever(aodToLockscreenTransitionViewModel.shortcutsAlpha)
             .thenReturn(intendedAlphaMutableStateFlow)
         whenever(dozingToLockscreenTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow())
@@ -263,7 +271,9 @@
         whenever(lockscreenToOccludedTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow())
         whenever(lockscreenToPrimaryBouncerTransitionViewModel.shortcutsAlpha)
             .thenReturn(emptyFlow())
-        whenever(shadeInteractor.qsExpansion).thenReturn(intendedShadeAlphaMutableStateFlow)
+        whenever(shadeInteractor.anyExpansion).thenReturn(intendedShadeAlphaMutableStateFlow)
+        whenever(transitionInteractor.finishedKeyguardState)
+            .thenReturn(intendedFinishedKeyguardStateFlow)
 
         underTest =
             KeyguardQuickAffordancesCombinedViewModel(
@@ -304,7 +314,8 @@
                 lockscreenToGoneTransitionViewModel = lockscreenToGoneTransitionViewModel,
                 lockscreenToOccludedTransitionViewModel = lockscreenToOccludedTransitionViewModel,
                 lockscreenToPrimaryBouncerTransitionViewModel =
-                    lockscreenToPrimaryBouncerTransitionViewModel
+                    lockscreenToPrimaryBouncerTransitionViewModel,
+                transitionInteractor = transitionInteractor,
             )
     }
 
@@ -682,6 +693,30 @@
             )
         }
 
+    @Test
+    fun shadeExpansionAlpha_changes_whenOnLockscreen() =
+        testScope.runTest {
+            intendedFinishedKeyguardStateFlow.value = KeyguardState.LOCKSCREEN
+            intendedShadeAlphaMutableStateFlow.value = 0.25f
+            val underTest = collectLastValue(underTest.transitionAlpha)
+            assertEquals(0.75f, underTest())
+
+            intendedShadeAlphaMutableStateFlow.value = 0.3f
+            assertEquals(0.7f, underTest())
+        }
+
+    @Test
+    fun shadeExpansionAlpha_alwaysZero_whenNotOnLockscreen() =
+        testScope.runTest {
+            intendedFinishedKeyguardStateFlow.value = KeyguardState.GONE
+            intendedShadeAlphaMutableStateFlow.value = 0.5f
+            val underTest = collectLastValue(underTest.transitionAlpha)
+            assertEquals(0f, underTest())
+
+            intendedShadeAlphaMutableStateFlow.value = 0.25f
+            assertEquals(0f, underTest())
+        }
+
     private suspend fun setUpQuickAffordanceModel(
         position: KeyguardQuickAffordancePosition,
         testConfig: TestConfig,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index c226790..b426d1d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -450,7 +450,7 @@
 
         mSetFlagsRule.enableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
 
-        // THEN touch should NOT be intercepted by NotificationShade
+        // THEN touch should be intercepted by NotificationShade
         assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isTrue()
     }
 
@@ -469,7 +469,35 @@
 
         mSetFlagsRule.enableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
 
-        // THEN touch should NOT be intercepted by NotificationShade
+        // THEN touch should be intercepted by NotificationShade
+        assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isTrue()
+    }
+
+    @Test
+    fun shouldInterceptTouchEvent_dozingAndPulsing_touchIntercepted() {
+        // GIVEN dozing
+        whenever(sysuiStatusBarStateController.isDozing).thenReturn(true)
+        // AND pulsing
+        whenever(dozeServiceHost.isPulsing()).thenReturn(true)
+        // AND status bar doesn't want it
+        whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT))
+            .thenReturn(false)
+        // AND shade is not fully expanded
+        whenever(notificationPanelViewController.isFullyExpanded()).thenReturn(false)
+        // AND the lock icon does NOT want the touch
+        whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT)).thenReturn(false)
+        // AND quick settings controller DOES want it
+        whenever(quickSettingsController.shouldQuickSettingsIntercept(any(), any(), any()))
+            .thenReturn(true)
+        // AND bouncer is not showing
+        whenever(centralSurfaces.isBouncerShowing()).thenReturn(false)
+        // AND panel view controller wants it
+        whenever(notificationPanelViewController.handleExternalInterceptTouch(DOWN_EVENT))
+            .thenReturn(true)
+
+        mSetFlagsRule.enableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
+
+        // THEN touch should be intercepted by NotificationShade
         assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isTrue()
     }
 
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/data/repository/FakeKeyguardTransitionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
index e20a0ab..a9a2d91 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
@@ -43,7 +43,7 @@
 class FakeKeyguardTransitionRepository @Inject constructor() : KeyguardTransitionRepository {
 
     private val _transitions =
-        MutableSharedFlow<TransitionStep>(replay = 2, onBufferOverflow = BufferOverflow.DROP_OLDEST)
+        MutableSharedFlow<TransitionStep>(replay = 3, onBufferOverflow = BufferOverflow.DROP_OLDEST)
     override val transitions: SharedFlow<TransitionStep> = _transitions
 
     init {
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/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
index 053ed77..3ecdf3f 100644
--- a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
+++ b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
@@ -36,9 +36,11 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
@@ -57,6 +59,7 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.graphics.Rect;
 import android.os.FileUtils;
 import android.os.ParcelFileDescriptor;
 import android.os.UserHandle;
@@ -78,6 +81,7 @@
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatcher;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -87,6 +91,7 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 import java.util.Optional;
 
 @RunWith(AndroidJUnit4.class)
@@ -815,6 +820,48 @@
                 WallpaperEventLogger.ERROR_LIVE_PACKAGE_NOT_INSTALLED);
     }
 
+    @Test
+    public void testOnRestore_noCropHints() throws Exception {
+        testParseCropHints(Map.of());
+    }
+
+    @Test
+    public void testOnRestore_singleCropHint() throws Exception {
+        Map<Integer, Rect> testMap = Map.of(WallpaperManager.PORTRAIT, new Rect(1, 2, 3, 4));
+        testParseCropHints(testMap);
+    }
+
+    @Test
+    public void testOnRestore_multipleCropHints() throws Exception {
+        Map<Integer, Rect> testMap = Map.of(
+                WallpaperManager.PORTRAIT, new Rect(1, 2, 3, 4),
+                WallpaperManager.SQUARE_PORTRAIT, new Rect(5, 6, 7, 8),
+                WallpaperManager.SQUARE_LANDSCAPE, new Rect(9, 10, 11, 12));
+        testParseCropHints(testMap);
+    }
+
+    private void testParseCropHints(Map<Integer, Rect> testMap) throws Exception {
+        assumeTrue(multiCrop());
+        mockRestoredStaticWallpaperFile(testMap);
+        mockStagedWallpaperFile(SYSTEM_WALLPAPER_STAGE);
+        mWallpaperBackupAgent.onCreate(USER_HANDLE, BackupAnnotations.BackupDestination.CLOUD,
+                BackupAnnotations.OperationType.RESTORE);
+
+        mWallpaperBackupAgent.onRestoreFinished();
+
+        ArgumentMatcher<SparseArray<Rect>> matcher = array -> {
+            boolean result = testMap.entrySet().stream().allMatch(entry -> {
+                int key = entry.getKey();
+                return (array.contains(key) && array.get(key).equals(testMap.get(key)));
+            });
+            for (int i = 0; i < array.size(); i++) {
+                if (!testMap.containsKey(array.keyAt(i))) result = false;
+            }
+            return result;
+        };
+        verify(mWallpaperManager).setStreamWithCrops(any(), argThat(matcher), eq(true), anyInt());
+    }
+
     private void mockCurrentWallpaperIds(int systemWallpaperId, int lockWallpaperId) {
         when(mWallpaperManager.getWallpaperId(eq(FLAG_SYSTEM))).thenReturn(systemWallpaperId);
         when(mWallpaperManager.getWallpaperId(eq(FLAG_LOCK))).thenReturn(lockWallpaperId);
@@ -880,6 +927,34 @@
         fstream.close();
     }
 
+    private void mockRestoredStaticWallpaperFile(Map<Integer, Rect> crops) throws Exception {
+        File wallpaperFile = new File(mContext.getFilesDir(), WALLPAPER_INFO_STAGE);
+        wallpaperFile.createNewFile();
+        FileOutputStream fstream = new FileOutputStream(wallpaperFile, false);
+        TypedXmlSerializer out = Xml.resolveSerializer(fstream);
+        out.startDocument(null, true);
+        out.startTag(null, "wp");
+        for (Map.Entry<Integer, Rect> entry: crops.entrySet()) {
+            String orientation = switch (entry.getKey()) {
+                case WallpaperManager.PORTRAIT -> "Portrait";
+                case WallpaperManager.LANDSCAPE -> "Landscape";
+                case WallpaperManager.SQUARE_PORTRAIT -> "SquarePortrait";
+                case WallpaperManager.SQUARE_LANDSCAPE -> "SquareLandscape";
+                default -> throw new IllegalArgumentException("Invalid orientation");
+            };
+            Rect rect = entry.getValue();
+            out.attributeInt(null, "cropLeft" + orientation, rect.left);
+            out.attributeInt(null, "cropTop" + orientation, rect.top);
+            out.attributeInt(null, "cropRight" + orientation, rect.right);
+            out.attributeInt(null, "cropBottom" + orientation, rect.bottom);
+        }
+        out.endTag(null, "wp");
+        out.endDocument();
+        fstream.flush();
+        FileUtils.sync(fstream);
+        fstream.close();
+    }
+
     private WallpaperInfo getFakeWallpaperInfo() throws Exception {
         Context context = InstrumentationRegistry.getTargetContext();
         Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
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/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index ef93400..cb6d26f 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -5673,7 +5673,7 @@
             final boolean isMuted = isStreamMutedByRingerOrZenMode(streamType);
             final boolean muteAllowedBySco =
                     !(shouldRingSco && streamType == AudioSystem.STREAM_RING);
-            final boolean shouldZenMute = shouldZenMuteStream(streamType);
+            final boolean shouldZenMute = isStreamAffectedByCurrentZen(streamType);
             final boolean shouldMute = shouldZenMute || (ringerModeMute
                     && isStreamAffectedByRingerMode(streamType) && muteAllowedBySco);
             if (isMuted == shouldMute) continue;
@@ -6937,24 +6937,8 @@
         return (mRingerModeAffectedStreams & (1 << streamType)) != 0;
     }
 
-    private boolean shouldZenMuteStream(int streamType) {
-        if (mNm.getZenMode() != Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {
-            return false;
-        }
-
-        NotificationManager.Policy zenPolicy = mNm.getConsolidatedNotificationPolicy();
-        final boolean muteAlarms = (zenPolicy.priorityCategories
-                & NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS) == 0;
-        final boolean muteMedia = (zenPolicy.priorityCategories
-                & NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA) == 0;
-        final boolean muteSystem = (zenPolicy.priorityCategories
-                & NotificationManager.Policy.PRIORITY_CATEGORY_SYSTEM) == 0;
-        final boolean muteNotificationAndRing = ZenModeConfig
-                .areAllPriorityOnlyRingerSoundsMuted(zenPolicy);
-        return muteAlarms && isAlarm(streamType)
-                || muteMedia && isMedia(streamType)
-                || muteSystem && isSystem(streamType)
-                || muteNotificationAndRing && isNotificationOrRinger(streamType);
+    public boolean isStreamAffectedByCurrentZen(int streamType) {
+        return (mZenModeAffectedStreams & (1 << streamType)) != 0;
     }
 
     private boolean isStreamMutedByRingerOrZenMode(int streamType) {
@@ -6962,11 +6946,9 @@
     }
 
     /**
-     * Notifications, ringer and system sounds are controlled by the ringer:
-     * {@link ZenModeHelper.RingerModeDelegate#getRingerModeAffectedStreams(int)} but can
-     * also be muted by DND based on the DND mode:
-     * DND total silence: media and alarms streams can be muted by DND
-     * DND alarms only: no streams additionally controlled by DND
+     * Volume streams can be muted based on the current DND state:
+     * DND total silence: ringer, notification, system, media and alarms streams muted by DND
+     * DND alarms only:  ringer, notification, system streams muted by DND
      * DND priority only: alarms, media, system, ringer and notification streams can be muted by
      * DND.  The current applied zenPolicy determines which streams will be muted by DND.
      * @return true if changed, else false
@@ -6976,12 +6958,20 @@
             return false;
         }
 
+        // If DND is off, no streams are muted by DND
         int zenModeAffectedStreams = 0;
         final int zenMode = mNm.getZenMode();
 
         if (zenMode == Settings.Global.ZEN_MODE_NO_INTERRUPTIONS) {
+            zenModeAffectedStreams |= 1 << AudioManager.STREAM_SYSTEM;
+            zenModeAffectedStreams |= 1 << AudioManager.STREAM_NOTIFICATION;
+            zenModeAffectedStreams |= 1 << AudioManager.STREAM_RING;
             zenModeAffectedStreams |= 1 << AudioManager.STREAM_ALARM;
             zenModeAffectedStreams |= 1 << AudioManager.STREAM_MUSIC;
+        } else if (zenMode == Settings.Global.ZEN_MODE_ALARMS) {
+            zenModeAffectedStreams |= 1 << AudioManager.STREAM_SYSTEM;
+            zenModeAffectedStreams |= 1 << AudioManager.STREAM_NOTIFICATION;
+            zenModeAffectedStreams |= 1 << AudioManager.STREAM_RING;
         } else if (zenMode == Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {
             NotificationManager.Policy zenPolicy = mNm.getConsolidatedNotificationPolicy();
             if ((zenPolicy.priorityCategories
@@ -7023,7 +7013,6 @@
                 ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)|
                  (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)),
                  UserHandle.USER_CURRENT);
-
         if (mIsSingleVolume) {
             ringerModeAffectedStreams = 0;
         } else if (mRingerModeDelegate != null) {
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/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 3e8af27..e7e8096 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -312,14 +312,15 @@
                 }
                 user.mPriorityStack.onSessionActiveStateChanged(record);
             }
-            boolean allowRunningInForeground = record.isActive()
-                    && (playbackState == null || playbackState.isActive());
+            boolean isUserEngaged =
+                    record.isActive() && (playbackState == null || playbackState.isActive());
 
             Log.d(TAG, "onSessionActiveStateChanged: "
                     + "record=" + record
                     + "playbackState=" + playbackState
-                    + "allowRunningInForeground=" + allowRunningInForeground);
-            setForegroundServiceAllowance(record, allowRunningInForeground);
+                    + "allowRunningInForeground=" + isUserEngaged);
+            setForegroundServiceAllowance(record, /* allowRunningInForeground= */ isUserEngaged);
+            reportMediaInteractionEvent(record, isUserEngaged);
             mHandler.postSessionsChanged(record);
         }
     }
@@ -417,12 +418,14 @@
             }
             user.mPriorityStack.onPlaybackStateChanged(record, shouldUpdatePriority);
             if (playbackState != null) {
-                boolean allowRunningInForeground = playbackState.isActive() && record.isActive();
+                boolean isUserEngaged = playbackState.isActive() && record.isActive();
                 Log.d(TAG, "onSessionPlaybackStateChanged: "
                         + "record=" + record
                         + "playbackState=" + playbackState
-                        + "allowRunningInForeground=" + allowRunningInForeground);
-                setForegroundServiceAllowance(record, allowRunningInForeground);
+                        + "allowRunningInForeground=" + isUserEngaged);
+                setForegroundServiceAllowance(
+                        record, /* allowRunningInForeground= */ isUserEngaged);
+                reportMediaInteractionEvent(record, isUserEngaged);
             }
         }
     }
@@ -590,6 +593,7 @@
 
         Log.d(TAG, "destroySessionLocked: record=" + session);
         setForegroundServiceAllowance(session, /* allowRunningInForeground= */ false);
+        reportMediaInteractionEvent(session, /* userEngaged= */ false);
         mHandler.postSessionsChanged(session);
     }
 
@@ -608,11 +612,9 @@
         if (allowRunningInForeground) {
             mActivityManagerInternal.startForegroundServiceDelegate(
                     foregroundServiceDelegationOptions, /* connection= */ null);
-            reportMediaInteractionEvent(record, /* userEngaged= */ true);
         } else {
             mActivityManagerInternal.stopForegroundServiceDelegate(
                     foregroundServiceDelegationOptions);
-            reportMediaInteractionEvent(record, /* userEngaged= */ false);
         }
     }
 
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 53ae60b..7455fe0 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -5720,6 +5720,14 @@
         }
 
         @Override
+        @Condition.State
+        public int getAutomaticZenRuleState(@NonNull String id) {
+            Objects.requireNonNull(id, "id is null");
+            enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRuleState");
+            return mZenModeHelper.getAutomaticZenRuleState(id);
+        }
+
+        @Override
         public void setAutomaticZenRuleState(String id, Condition condition) {
             Objects.requireNonNull(id, "id is null");
             Objects.requireNonNull(condition, "Condition is null");
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 54de197..efb8c84 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -856,6 +856,20 @@
         }
     }
 
+    @Condition.State
+    int getAutomaticZenRuleState(String id) {
+        synchronized (mConfigLock) {
+            if (mConfig == null) {
+                return Condition.STATE_UNKNOWN;
+            }
+            ZenRule rule = mConfig.automaticRules.get(id);
+            if (rule == null || !canManageAutomaticZenRule(rule)) {
+                return Condition.STATE_UNKNOWN;
+            }
+            return rule.condition != null ? rule.condition.state : Condition.STATE_FALSE;
+        }
+    }
+
     void setAutomaticZenRuleState(String id, Condition condition, @ConfigChangeOrigin int origin,
             int callingUid) {
         requirePublicOrigin("setAutomaticZenRuleState", origin);
diff --git a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
new file mode 100644
index 0000000..a4c4347
--- /dev/null
+++ b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
@@ -0,0 +1,419 @@
+/*
+ * 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.ondeviceintelligence;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.app.AppGlobals;
+import android.app.ondeviceintelligence.Content;
+import android.app.ondeviceintelligence.DownloadCallback;
+import android.app.ondeviceintelligence.Feature;
+import android.app.ondeviceintelligence.IDownloadCallback;
+import android.app.ondeviceintelligence.IFeatureCallback;
+import android.app.ondeviceintelligence.IFeatureDetailsCallback;
+import android.app.ondeviceintelligence.IListFeaturesCallback;
+import android.app.ondeviceintelligence.IOnDeviceIntelligenceManager;
+import android.app.ondeviceintelligence.IProcessingSignal;
+import android.app.ondeviceintelligence.IResponseCallback;
+import android.app.ondeviceintelligence.IStreamingResponseCallback;
+import android.app.ondeviceintelligence.ITokenCountCallback;
+import android.app.ondeviceintelligence.OnDeviceIntelligenceManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.os.ICancellationSignal;
+import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.DeviceConfig;
+import android.service.ondeviceintelligence.IOnDeviceTrustedInferenceService;
+import android.service.ondeviceintelligence.IRemoteStorageService;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.infra.AndroidFuture;
+import com.android.internal.infra.ServiceConnector;
+import com.android.internal.os.BackgroundThread;
+import com.android.server.SystemService;
+
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * This is the system service for handling calls on the {@link OnDeviceIntelligenceManager}. This
+ * service holds connection references to the underlying remote services i.e. the isolated service
+ * {@link android.service.ondeviceintelligence.OnDeviceTrustedInferenceService} and a regular
+ * service counter part {@link android.service.ondeviceintelligence.OnDeviceIntelligenceService}.
+ *
+ * Note: Both the remote services run under the SYSTEM user, as we cannot have separate instance of
+ * the Inference service for each user, due to possible high memory footprint.
+ *
+ * @hide
+ */
+public class OnDeviceIntelligenceManagerService extends SystemService {
+
+    private static final String TAG = OnDeviceIntelligenceManagerService.class.getSimpleName();
+    private static final String KEY_SERVICE_ENABLED = "service_enabled";
+
+    /** Default value in absence of {@link DeviceConfig} override. */
+    private static final boolean DEFAULT_SERVICE_ENABLED = true;
+    private static final String NAMESPACE_ON_DEVICE_INTELLIGENCE = "ondeviceintelligence";
+
+    private final Context mContext;
+    protected final Object mLock = new Object();
+
+
+    private RemoteOnDeviceTrustedInferenceService mRemoteInferenceService;
+    private RemoteOnDeviceIntelligenceService mRemoteOnDeviceIntelligenceService;
+    volatile boolean mIsServiceEnabled;
+
+    public OnDeviceIntelligenceManagerService(Context context) {
+        super(context);
+        mContext = context;
+    }
+
+    @Override
+    public void onStart() {
+        publishBinderService(
+                Context.ON_DEVICE_INTELLIGENCE_SERVICE, new OnDeviceIntelligenceManagerInternal(),
+                /* allowIsolated = */true);
+    }
+
+
+    @Override
+    public void onBootPhase(int phase) {
+        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
+            DeviceConfig.addOnPropertiesChangedListener(
+                    NAMESPACE_ON_DEVICE_INTELLIGENCE,
+                    BackgroundThread.getExecutor(),
+                    (properties) -> onDeviceConfigChange(properties.getKeyset()));
+
+            mIsServiceEnabled = isServiceEnabled();
+        }
+    }
+
+    private void onDeviceConfigChange(@NonNull Set<String> keys) {
+        if (keys.contains(KEY_SERVICE_ENABLED)) {
+            mIsServiceEnabled = isServiceEnabled();
+        }
+    }
+
+    private boolean isServiceEnabled() {
+        return DeviceConfig.getBoolean(
+                NAMESPACE_ON_DEVICE_INTELLIGENCE,
+                KEY_SERVICE_ENABLED, DEFAULT_SERVICE_ENABLED);
+    }
+
+    private final class OnDeviceIntelligenceManagerInternal extends
+            IOnDeviceIntelligenceManager.Stub {
+        @Override
+        public void getVersion(RemoteCallback remoteCallback) throws RemoteException {
+            Slog.i(TAG, "OnDeviceIntelligenceManagerInternal getVersion");
+            Objects.requireNonNull(remoteCallback);
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
+            if (!mIsServiceEnabled) {
+                Slog.w(TAG, "Service not available");
+                remoteCallback.sendResult(null);
+                return;
+            }
+            ensureRemoteIntelligenceServiceInitialized();
+            mRemoteOnDeviceIntelligenceService.post(
+                    service -> service.getVersion(remoteCallback));
+        }
+
+        @Override
+        public void getFeature(int id, IFeatureCallback featureCallback) throws RemoteException {
+            Slog.i(TAG, "OnDeviceIntelligenceManagerInternal getFeatures");
+            Objects.requireNonNull(featureCallback);
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
+            if (!mIsServiceEnabled) {
+                Slog.w(TAG, "Service not available");
+                featureCallback.onFailure(
+                        OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException.ON_DEVICE_INTELLIGENCE_SERVICE_UNAVAILABLE,
+                        "OnDeviceIntelligenceManagerService is unavailable",
+                        new PersistableBundle());
+                return;
+            }
+            ensureRemoteIntelligenceServiceInitialized();
+            mRemoteOnDeviceIntelligenceService.post(
+                    service -> service.getFeature(id, featureCallback));
+        }
+
+        @Override
+        public void listFeatures(IListFeaturesCallback listFeaturesCallback)
+                throws RemoteException {
+            Slog.i(TAG, "OnDeviceIntelligenceManagerInternal getFeatures");
+            Objects.requireNonNull(listFeaturesCallback);
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
+            if (!mIsServiceEnabled) {
+                Slog.w(TAG, "Service not available");
+                listFeaturesCallback.onFailure(
+                        OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException.ON_DEVICE_INTELLIGENCE_SERVICE_UNAVAILABLE,
+                        "OnDeviceIntelligenceManagerService is unavailable",
+                        new PersistableBundle());
+                return;
+            }
+            ensureRemoteIntelligenceServiceInitialized();
+            mRemoteOnDeviceIntelligenceService.post(
+                    service -> service.listFeatures(listFeaturesCallback));
+        }
+
+        @Override
+        public void getFeatureDetails(Feature feature,
+                IFeatureDetailsCallback featureDetailsCallback)
+                throws RemoteException {
+            Slog.i(TAG, "OnDeviceIntelligenceManagerInternal getFeatureStatus");
+            Objects.requireNonNull(feature);
+            Objects.requireNonNull(featureDetailsCallback);
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
+            if (!mIsServiceEnabled) {
+                Slog.w(TAG, "Service not available");
+                featureDetailsCallback.onFailure(
+                        OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException.ON_DEVICE_INTELLIGENCE_SERVICE_UNAVAILABLE,
+                        "OnDeviceIntelligenceManagerService is unavailable",
+                        new PersistableBundle());
+                return;
+            }
+            ensureRemoteIntelligenceServiceInitialized();
+            mRemoteOnDeviceIntelligenceService.post(
+                    service -> service.getFeatureDetails(feature, featureDetailsCallback));
+        }
+
+        @Override
+        public void requestFeatureDownload(Feature feature, ICancellationSignal cancellationSignal,
+                IDownloadCallback downloadCallback) throws RemoteException {
+            Slog.i(TAG, "OnDeviceIntelligenceManagerInternal requestFeatureDownload");
+            Objects.requireNonNull(feature);
+            Objects.requireNonNull(downloadCallback);
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
+            if (!mIsServiceEnabled) {
+                Slog.w(TAG, "Service not available");
+                downloadCallback.onDownloadFailed(
+                        DownloadCallback.DOWNLOAD_FAILURE_STATUS_UNAVAILABLE,
+                        "OnDeviceIntelligenceManagerService is unavailable",
+                        new PersistableBundle());
+            }
+            ensureRemoteIntelligenceServiceInitialized();
+            mRemoteOnDeviceIntelligenceService.post(
+                    service -> service.requestFeatureDownload(feature, cancellationSignal,
+                            downloadCallback));
+        }
+
+
+        @Override
+        public void requestTokenCount(Feature feature,
+                Content request, ICancellationSignal cancellationSignal,
+                ITokenCountCallback tokenCountcallback) throws RemoteException {
+            Slog.i(TAG, "OnDeviceIntelligenceManagerInternal prepareFeatureProcessing");
+            Objects.requireNonNull(feature);
+            Objects.requireNonNull(request);
+            Objects.requireNonNull(tokenCountcallback);
+
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
+            if (!mIsServiceEnabled) {
+                Slog.w(TAG, "Service not available");
+                tokenCountcallback.onFailure(
+                        OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException.ON_DEVICE_INTELLIGENCE_SERVICE_UNAVAILABLE,
+                        "OnDeviceIntelligenceManagerService is unavailable",
+                        new PersistableBundle());
+            }
+            ensureRemoteTrustedInferenceServiceInitialized();
+            mRemoteInferenceService.post(
+                    service -> service.requestTokenCount(feature, request, cancellationSignal,
+                            tokenCountcallback));
+        }
+
+        @Override
+        public void processRequest(Feature feature,
+                Content request,
+                int requestType,
+                ICancellationSignal cancellationSignal,
+                IProcessingSignal processingSignal,
+                IResponseCallback responseCallback)
+                throws RemoteException {
+            Slog.i(TAG, "OnDeviceIntelligenceManagerInternal processRequest");
+            Objects.requireNonNull(feature);
+            Objects.requireNonNull(responseCallback);
+            Objects.requireNonNull(request);
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
+            if (!mIsServiceEnabled) {
+                Slog.w(TAG, "Service not available");
+                responseCallback.onFailure(
+                        OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException.PROCESSING_ERROR_SERVICE_UNAVAILABLE,
+                        "OnDeviceIntelligenceManagerService is unavailable",
+                        new PersistableBundle());
+            }
+            ensureRemoteTrustedInferenceServiceInitialized();
+            mRemoteInferenceService.post(
+                    service -> service.processRequest(feature, request, requestType,
+                            cancellationSignal, processingSignal,
+                            responseCallback));
+        }
+
+        @Override
+        public void processRequestStreaming(Feature feature,
+                Content request,
+                int requestType,
+                ICancellationSignal cancellationSignal,
+                IProcessingSignal processingSignal,
+                IStreamingResponseCallback streamingCallback) throws RemoteException {
+            Slog.i(TAG, "OnDeviceIntelligenceManagerInternal processRequestStreaming");
+            Objects.requireNonNull(feature);
+            Objects.requireNonNull(request);
+            Objects.requireNonNull(streamingCallback);
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
+            if (!mIsServiceEnabled) {
+                Slog.w(TAG, "Service not available");
+                streamingCallback.onFailure(
+                        OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException.PROCESSING_ERROR_SERVICE_UNAVAILABLE,
+                        "OnDeviceIntelligenceManagerService is unavailable",
+                        new PersistableBundle());
+            }
+            ensureRemoteTrustedInferenceServiceInitialized();
+            mRemoteInferenceService.post(
+                    service -> service.processRequestStreaming(feature, request, requestType,
+                            cancellationSignal, processingSignal,
+                            streamingCallback));
+        }
+    }
+
+    private void ensureRemoteIntelligenceServiceInitialized() throws RemoteException {
+        synchronized (mLock) {
+            if (mRemoteOnDeviceIntelligenceService == null) {
+                String serviceName = mContext.getResources().getString(
+                        R.string.config_defaultOnDeviceIntelligenceService);
+                validateService(serviceName, false);
+                mRemoteOnDeviceIntelligenceService = new RemoteOnDeviceIntelligenceService(mContext,
+                        ComponentName.unflattenFromString(serviceName),
+                        UserHandle.SYSTEM.getIdentifier());
+            }
+        }
+    }
+
+    private void ensureRemoteTrustedInferenceServiceInitialized() throws RemoteException {
+        synchronized (mLock) {
+            if (mRemoteInferenceService == null) {
+                String serviceName = mContext.getResources().getString(
+                        R.string.config_defaultOnDeviceTrustedInferenceService);
+                validateService(serviceName, true);
+                mRemoteInferenceService = new RemoteOnDeviceTrustedInferenceService(mContext,
+                        ComponentName.unflattenFromString(serviceName),
+                        UserHandle.SYSTEM.getIdentifier());
+                mRemoteInferenceService.setServiceLifecycleCallbacks(
+                        new ServiceConnector.ServiceLifecycleCallbacks<>() {
+                            @Override
+                            public void onConnected(
+                                    @NonNull IOnDeviceTrustedInferenceService service) {
+                                try {
+                                    service.registerRemoteStorageService(
+                                            getIRemoteStorageService());
+                                } catch (RemoteException ex) {
+                                    Slog.w(TAG, "Failed to send connected event", ex);
+                                }
+                            }
+                        });
+            }
+        }
+    }
+
+    @NonNull
+    private IRemoteStorageService.Stub getIRemoteStorageService() {
+        return new IRemoteStorageService.Stub() {
+            @Override
+            public void getReadOnlyFileDescriptor(
+                    String filePath,
+                    AndroidFuture<ParcelFileDescriptor> future) {
+                mRemoteOnDeviceIntelligenceService.post(
+                        service -> service.getReadOnlyFileDescriptor(
+                                filePath, future));
+            }
+
+            @Override
+            public void getReadOnlyFeatureFileDescriptorMap(
+                    Feature feature,
+                    RemoteCallback remoteCallback)
+                    throws RemoteException {
+                mRemoteOnDeviceIntelligenceService.post(
+                        service -> service.getReadOnlyFeatureFileDescriptorMap(
+                                feature, remoteCallback));
+            }
+        };
+    }
+
+    @GuardedBy("mLock")
+    private void validateService(String serviceName, boolean checkIsolated)
+            throws RemoteException {
+        if (TextUtils.isEmpty(serviceName)) {
+            throw new RuntimeException("");
+        }
+        ComponentName serviceComponent = ComponentName.unflattenFromString(
+                serviceName);
+        ServiceInfo serviceInfo = AppGlobals.getPackageManager().getServiceInfo(
+                serviceComponent,
+                PackageManager.MATCH_DIRECT_BOOT_AWARE
+                        | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, 0);
+        if (serviceInfo != null) {
+            if (!checkIsolated) {
+                checkServiceRequiresPermission(serviceInfo,
+                        Manifest.permission.BIND_ON_DEVICE_INTELLIGENCE_SERVICE);
+                return;
+            }
+
+            checkServiceRequiresPermission(serviceInfo,
+                    Manifest.permission.BIND_ON_DEVICE_TRUSTED_SERVICE);
+            if (!isIsolatedService(serviceInfo)) {
+                throw new SecurityException(
+                        "Call required an isolated service, but the configured service: "
+                                + serviceName + ", is not isolated");
+            }
+        } else {
+            throw new RuntimeException(
+                    "Could not find service info for serviceName: " + serviceName);
+        }
+    }
+
+    private static void checkServiceRequiresPermission(ServiceInfo serviceInfo,
+            String requiredPermission) {
+        final String permission = serviceInfo.permission;
+        if (!requiredPermission.equals(permission)) {
+            throw new SecurityException(String.format(
+                    "Service %s requires %s permission. Found %s permission",
+                    serviceInfo.getComponentName(),
+                    requiredPermission,
+                    serviceInfo.permission));
+        }
+    }
+
+    @GuardedBy("mLock")
+    private boolean isIsolatedService(@NonNull ServiceInfo serviceInfo) {
+        return (serviceInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0
+                && (serviceInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) == 0;
+    }
+}
diff --git a/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java b/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java
new file mode 100644
index 0000000..48258d7
--- /dev/null
+++ b/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java
@@ -0,0 +1,56 @@
+/*
+ * 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.ondeviceintelligence;
+
+import static android.content.Context.BIND_FOREGROUND_SERVICE;
+import static android.content.Context.BIND_INCLUDE_CAPABILITIES;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.service.ondeviceintelligence.IOnDeviceIntelligenceService;
+import android.service.ondeviceintelligence.OnDeviceIntelligenceService;
+
+import com.android.internal.infra.ServiceConnector;
+
+/**
+ * Manages the connection to the remote on-device intelligence service. Also, handles unbinding
+ * logic set by the service implementation via a Secure Settings flag.
+ */
+public class RemoteOnDeviceIntelligenceService extends
+        ServiceConnector.Impl<IOnDeviceIntelligenceService> {
+    private static final String TAG =
+            RemoteOnDeviceIntelligenceService.class.getSimpleName();
+
+    RemoteOnDeviceIntelligenceService(Context context, ComponentName serviceName,
+            int userId) {
+        super(context, new Intent(
+                        OnDeviceIntelligenceService.SERVICE_INTERFACE).setComponent(serviceName),
+                BIND_FOREGROUND_SERVICE | BIND_INCLUDE_CAPABILITIES, userId,
+                IOnDeviceIntelligenceService.Stub::asInterface);
+
+        // Bind right away
+        connect();
+    }
+
+    @Override
+    protected long getAutoDisconnectTimeoutMs() {
+        // Disable automatic unbinding.
+        // TODO: add logic to fetch this flag via SecureSettings.
+        return -1;
+    }
+}
diff --git a/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceTrustedInferenceService.java b/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceTrustedInferenceService.java
new file mode 100644
index 0000000..cc8e788
--- /dev/null
+++ b/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceTrustedInferenceService.java
@@ -0,0 +1,64 @@
+/*
+ * 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.ondeviceintelligence;
+
+import static android.content.Context.BIND_FOREGROUND_SERVICE;
+import static android.content.Context.BIND_INCLUDE_CAPABILITIES;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.service.ondeviceintelligence.IOnDeviceTrustedInferenceService;
+import android.service.ondeviceintelligence.OnDeviceTrustedInferenceService;
+
+import com.android.internal.infra.ServiceConnector;
+
+
+/**
+ * Manages the connection to the remote on-device trusted inference service. Also, handles unbinding
+ * logic set by the service implementation via a SecureSettings flag.
+ */
+public class RemoteOnDeviceTrustedInferenceService extends
+        ServiceConnector.Impl<IOnDeviceTrustedInferenceService> {
+    /**
+     * Creates an instance of {@link ServiceConnector}
+     *
+     * See {@code protected} methods for optional parameters you can override.
+     *
+     * @param context to be used for {@link Context#bindServiceAsUser binding} and
+     *                {@link Context#unbindService unbinding}
+     * @param userId  to be used for {@link Context#bindServiceAsUser binding}
+     */
+    RemoteOnDeviceTrustedInferenceService(Context context, ComponentName serviceName,
+            int userId) {
+        super(context, new Intent(
+                        OnDeviceTrustedInferenceService.SERVICE_INTERFACE).setComponent(serviceName),
+                BIND_FOREGROUND_SERVICE | BIND_INCLUDE_CAPABILITIES, userId,
+                IOnDeviceTrustedInferenceService.Stub::asInterface);
+
+        // Bind right away
+        connect();
+    }
+
+
+    @Override
+    protected long getAutoDisconnectTimeoutMs() {
+        // Disable automatic unbinding.
+        // TODO: add logic to fetch this flag via SecureSettings.
+        return -1;
+    }
+}
diff --git a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
index 524bad5..b6daed1 100644
--- a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
+++ b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
@@ -30,6 +30,7 @@
 import android.content.pm.IBackgroundInstallControlService;
 import android.content.pm.InstallSourceInfo;
 import android.content.pm.PackageInfo;
+import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.ParceledListSlice;
@@ -46,6 +47,7 @@
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.AtomicFile;
+import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArrayMap;
 import android.util.SparseSetArray;
@@ -63,8 +65,10 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Comparator;
 import java.util.List;
 import java.util.ListIterator;
+import java.util.Optional;
 import java.util.Set;
 import java.util.TreeSet;
 
@@ -103,6 +107,24 @@
     private final SparseArrayMap<String, TreeSet<ForegroundTimeFrame>>
             mInstallerForegroundTimeFrames = new SparseArrayMap<>();
 
+    @VisibleForTesting
+    protected final PackageManagerInternal.PackageListObserver mPackageObserver =
+            new PackageManagerInternal.PackageListObserver() {
+                @Override
+                public void onPackageAdded(String packageName, int uid) {
+                    final int userId = UserHandle.getUserId(uid);
+                    mHandler.obtainMessage(MSG_PACKAGE_ADDED, userId, 0, packageName)
+                            .sendToTarget();
+                }
+
+                @Override
+                public void onPackageRemoved(String packageName, int uid) {
+                    final int userId = UserHandle.getUserId(uid);
+                    mHandler.obtainMessage(MSG_PACKAGE_REMOVED, userId, 0, packageName)
+                            .sendToTarget();
+                }
+            };
+
     public BackgroundInstallControlService(@NonNull Context context) {
         this(new InjectorImpl(context));
     }
@@ -258,6 +280,7 @@
 
         String installerPackageName;
         String initiatingPackageName;
+
         try {
             final InstallSourceInfo installInfo = mPackageManager.getInstallSourceInfo(packageName);
             installerPackageName = installInfo.getInstallingPackageName();
@@ -280,7 +303,8 @@
 
         // convert up-time to current time.
         final long installTimestamp =
-                System.currentTimeMillis() - (SystemClock.uptimeMillis() - appInfo.createTimestamp);
+                System.currentTimeMillis() - (SystemClock.uptimeMillis()
+                        - retrieveInstallStartTimestamp(packageName, userId, appInfo));
 
         if (installedByAdb(initiatingPackageName)
                 || wasForegroundInstallation(installerPackageName, userId, installTimestamp)) {
@@ -293,6 +317,35 @@
         writeBackgroundInstalledPackagesToDisk();
     }
 
+    private long retrieveInstallStartTimestamp(String packageName,
+                                               int userId, ApplicationInfo appInfo) {
+        long installStartTimestamp = appInfo.createTimestamp;
+
+        try {
+            Optional<PackageInstaller.SessionInfo> latestInstallSession =
+                    getLatestInstallSession(packageName, userId);
+            if (latestInstallSession.isEmpty()) {
+                Slog.w(TAG, "Package's historical install session not found, falling back "
+                        + "to appInfo.createTimestamp: " + packageName);
+            } else {
+                installStartTimestamp = latestInstallSession.get().getCreatedMillis();
+            }
+        } catch (Exception e) {
+            Slog.w(TAG, "Retrieval of install time from historical session failed, falling "
+                    + "back to appInfo.createTimestamp");
+            Slog.w(TAG, Log.getStackTraceString(e));
+        }
+        return installStartTimestamp;
+    }
+
+    private Optional<PackageInstaller.SessionInfo> getLatestInstallSession(
+            String packageName, int userId) {
+        List<PackageInstaller.SessionInfo> historicalSessions =
+                mPackageManagerInternal.getHistoricalSessions(userId).getList();
+        return historicalSessions.stream().filter(s -> packageName.equals(s.getAppPackageName()))
+                .max(Comparator.comparingLong(PackageInstaller.SessionInfo::getCreatedMillis));
+    }
+
     // ADB sets installerPackageName to null, this creates a loophole to bypass BIC which will be
     // addressed with b/265203007
     private boolean installedByAdb(String initiatingPackageName) {
@@ -496,22 +549,7 @@
             publishBinderService(Context.BACKGROUND_INSTALL_CONTROL_SERVICE, mBinderService);
         }
 
-        mPackageManagerInternal.getPackageList(
-                new PackageManagerInternal.PackageListObserver() {
-                    @Override
-                    public void onPackageAdded(String packageName, int uid) {
-                        final int userId = UserHandle.getUserId(uid);
-                        mHandler.obtainMessage(MSG_PACKAGE_ADDED, userId, 0, packageName)
-                                .sendToTarget();
-                    }
-
-                    @Override
-                    public void onPackageRemoved(String packageName, int uid) {
-                        final int userId = UserHandle.getUserId(uid);
-                        mHandler.obtainMessage(MSG_PACKAGE_REMOVED, userId, 0, packageName)
-                                .sendToTarget();
-                    }
-                });
+        mPackageManagerInternal.getPackageList(mPackageObserver);
     }
 
     // The foreground time frame (ForegroundTimeFrame) represents the period
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 76d87ff..d8d8dd2 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -350,18 +350,50 @@
         public void registerPackageInstallerCallback(String callingPackage,
                 IPackageInstallerCallback callback) {
             verifyCallingPackage(callingPackage);
-            UserHandle callingIdUserHandle = new UserHandle(getCallingUserId());
-            getPackageInstallerService().registerCallback(callback, eventUserId ->
-                            isEnabledProfileOf(callingIdUserHandle,
-                                    new UserHandle(eventUserId), "shouldReceiveEvent"));
+            BroadcastCookie callerInfo =
+                    new BroadcastCookie(
+                            new UserHandle(getCallingUserId()),
+                            callingPackage,
+                            getCallingPid(),
+                            getCallingUid());
+            getPackageInstallerService()
+                    .registerCallback(
+                            callback,
+                            eventUserId ->
+                                    isEnabledProfileOf(
+                                            callerInfo,
+                                            new UserHandle(eventUserId),
+                                            "shouldReceiveEvent"));
+        }
+
+        @Override
+        public List<UserHandle> getUserProfiles() {
+            int[] userIds;
+            if (!canAccessHiddenProfile(getCallingUid(), getCallingPid())) {
+                userIds = mUm.getProfileIdsExcludingHidden(getCallingUserId(), /* enabled= */ true);
+            } else {
+                userIds = mUm.getEnabledProfileIds(getCallingUserId());
+            }
+            final List<UserHandle> result = new ArrayList<>(userIds.length);
+            for (int userId : userIds) {
+                result.add(UserHandle.of(userId));
+            }
+            return result;
         }
 
         @Override
         public ParceledListSlice<SessionInfo> getAllSessions(String callingPackage) {
             verifyCallingPackage(callingPackage);
             List<SessionInfo> sessionInfos = new ArrayList<>();
-            int[] userIds = mUm.getEnabledProfileIds(getCallingUserId());
             final int callingUid = Binder.getCallingUid();
+
+            int[] userIds;
+            if (!canAccessHiddenProfile(callingUid, Binder.getCallingPid())) {
+                userIds = mUm.getProfileIdsExcludingHidden(getCallingUserId(), /* enabled= */ true);
+            } else {
+                userIds = mUm.getEnabledProfileIds(getCallingUserId());
+            }
+
             final long token = Binder.clearCallingIdentity();
             try {
                 for (int userId : userIds) {
@@ -389,7 +421,7 @@
                     mPackageInstallerService = ((PackageInstallerService) ((IPackageManager)
                             ServiceManager.getService("package")).getPackageInstaller());
                 } catch (RemoteException e) {
-                    Slog.wtf(TAG, "Error gettig IPackageInstaller", e);
+                    Slog.wtf(TAG, "Error getting IPackageInstaller", e);
                 }
             }
             return mPackageInstallerService;
@@ -470,57 +502,86 @@
                             + targetUserId + " from " + callingUserId + " not allowed");
                     return false;
                 }
-
-                if (areHiddenApisChecksEnabled()
-                        && mUm.getUserProperties(UserHandle.of(targetUserId))
-                                        .getProfileApiVisibility()
-                                == UserProperties.PROFILE_API_VISIBILITY_HIDDEN
-                        && !canAccessHiddenProfileInjected(callingUid, callingPid)) {
-                    return false;
-                }
             } finally {
                 injectRestoreCallingIdentity(ident);
             }
 
+            if (isHiddenProfile(UserHandle.of(targetUserId))
+                    && !canAccessHiddenProfile(callingUid, callingPid)) {
+                return false;
+            }
+
             return mUserManagerInternal.isProfileAccessible(callingUserId, targetUserId,
                     message, true);
         }
 
-        boolean areHiddenApisChecksEnabled() {
-            return android.os.Flags.allowPrivateProfile()
-                    && Flags.enableLauncherAppsHiddenProfileChecks()
-                    && Flags.enablePermissionToAccessHiddenProfiles();
+        private boolean isHiddenProfile(UserHandle targetUser) {
+            if (!Flags.enableLauncherAppsHiddenProfileChecks()) {
+                return false;
+            }
+
+            long identity = injectClearCallingIdentity();
+            try {
+                UserProperties properties = mUm.getUserProperties(targetUser);
+                if (properties == null) {
+                    return false;
+                }
+
+                return properties.getProfileApiVisibility()
+                        == UserProperties.PROFILE_API_VISIBILITY_HIDDEN;
+            } catch (IllegalArgumentException e) {
+                return false;
+            } finally {
+                injectRestoreCallingIdentity(identity);
+            }
         }
 
         private void verifyCallingPackage(String callingPackage) {
             verifyCallingPackage(callingPackage, injectBinderCallingUid());
         }
 
-        boolean canAccessHiddenProfileInjected(int callingUid, int callingPid) {
-            AndroidPackage callingPackage = mPackageManagerInternal.getPackage(callingUid);
-            if (callingPackage == null) {
-                return false;
-            }
-
-            if (!mRoleManager
-                    .getRoleHoldersAsUser(
-                            RoleManager.ROLE_HOME, UserHandle.getUserHandleForUid(callingUid))
-                    .contains(callingPackage.getPackageName())) {
-                return false;
-            }
-
-            if (mContext.checkPermission(
-                            Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL, callingPid, callingUid)
-                    == PackageManager.PERMISSION_GRANTED) {
+        private boolean canAccessHiddenProfile(int callingUid, int callingPid) {
+            if (!areHiddenApisChecksEnabled()) {
                 return true;
             }
 
-            // TODO(b/321988638): add option to disable with a flag
-            return mContext.checkPermission(
-                            android.Manifest.permission.ACCESS_HIDDEN_PROFILES,
-                            callingPid,
-                            callingUid)
-                    == PackageManager.PERMISSION_GRANTED;
+            long ident = injectClearCallingIdentity();
+            try {
+                AndroidPackage callingPackage = mPackageManagerInternal.getPackage(callingUid);
+                if (callingPackage == null) {
+                    return false;
+                }
+
+                if (!mRoleManager
+                        .getRoleHoldersAsUser(
+                                RoleManager.ROLE_HOME, UserHandle.getUserHandleForUid(callingUid))
+                        .contains(callingPackage.getPackageName())) {
+                    return false;
+                }
+                if (mContext.checkPermission(
+                                Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL,
+                                callingPid,
+                                callingUid)
+                        == PackageManager.PERMISSION_GRANTED) {
+                    return true;
+                }
+
+                // TODO(b/321988638): add option to disable with a flag
+                return mContext.checkPermission(
+                                android.Manifest.permission.ACCESS_HIDDEN_PROFILES,
+                                callingPid,
+                                callingUid)
+                        == PackageManager.PERMISSION_GRANTED;
+            } finally {
+                injectRestoreCallingIdentity(ident);
+            }
+        }
+
+        private boolean areHiddenApisChecksEnabled() {
+            return android.os.Flags.allowPrivateProfile()
+                    && Flags.enableHidingProfiles()
+                    && Flags.enableLauncherAppsHiddenProfileChecks()
+                    && Flags.enablePermissionToAccessHiddenProfiles();
         }
 
         @VisibleForTesting // We override it in unit tests
@@ -2068,12 +2129,18 @@
                     });
         }
 
-        /** Checks if user is a profile of or same as listeningUser.
-         * and the user is enabled. */
-        private boolean isEnabledProfileOf(UserHandle listeningUser, UserHandle user,
-                String debugMsg) {
-            return mUserManagerInternal.isProfileAccessible(listeningUser.getIdentifier(),
-                    user.getIdentifier(), debugMsg, false);
+        /**
+         * Checks if user is a profile of or same as listeningUser and the target user is enabled
+         * and accessible for caller.
+         */
+        private boolean isEnabledProfileOf(
+                BroadcastCookie cookie, UserHandle user, String debugMsg) {
+            if (isHiddenProfile(user)
+                    && !canAccessHiddenProfile(cookie.callingUid, cookie.callingPid)) {
+                return false;
+            }
+            return mUserManagerInternal.isProfileAccessible(
+                    cookie.user.getIdentifier(), user.getIdentifier(), debugMsg, false);
         }
 
         /**
@@ -2305,7 +2372,7 @@
                                         mListeners.getBroadcastItem(i);
                                 final BroadcastCookie cookie =
                                         (BroadcastCookie) mListeners.getBroadcastCookie(i);
-                                if (!isEnabledProfileOf(cookie.user, user, "onPackageRemoved")) {
+                                if (!isEnabledProfileOf(cookie, user, "onPackageRemoved")) {
                                     continue;
                                 }
                                 if (!isCallingAppIdAllowed(appIdAllowList, UserHandle.getAppId(
@@ -2344,7 +2411,7 @@
                     for (int i = 0; i < n; i++) {
                         IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
                         BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
-                        if (!isEnabledProfileOf(cookie.user, user, "onPackageAdded")) {
+                        if (!isEnabledProfileOf(cookie, user, "onPackageAdded")) {
                             continue;
                         }
                         if (!isPackageVisibleToListener(packageName, cookie, user)) {
@@ -2378,7 +2445,7 @@
                     for (int i = 0; i < n; i++) {
                         IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
                         BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
-                        if (!isEnabledProfileOf(cookie.user, user, "onPackageModified")) {
+                        if (!isEnabledProfileOf(cookie, user, "onPackageModified")) {
                             continue;
                         }
                         if (!isPackageVisibleToListener(packageName, cookie, user)) {
@@ -2403,7 +2470,7 @@
                     for (int i = 0; i < n; i++) {
                         IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
                         BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
-                        if (!isEnabledProfileOf(cookie.user, user, "onPackagesAvailable")) {
+                        if (!isEnabledProfileOf(cookie, user, "onPackagesAvailable")) {
                             continue;
                         }
                         final String[] filteredPackages =
@@ -2433,7 +2500,7 @@
                     for (int i = 0; i < n; i++) {
                         IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
                         BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
-                        if (!isEnabledProfileOf(cookie.user, user, "onPackagesUnavailable")) {
+                        if (!isEnabledProfileOf(cookie, user, "onPackagesUnavailable")) {
                             continue;
                         }
                         final String[] filteredPackages =
@@ -2477,7 +2544,7 @@
                     for (int i = 0; i < n; i++) {
                         IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
                         BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
-                        if (!isEnabledProfileOf(cookie.user, user, "onPackagesSuspended")) {
+                        if (!isEnabledProfileOf(cookie, user, "onPackagesSuspended")) {
                             continue;
                         }
                         final String[] filteredPackagesWithoutExtras =
@@ -2514,7 +2581,7 @@
                     for (int i = 0; i < n; i++) {
                         IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
                         BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
-                        if (!isEnabledProfileOf(cookie.user, user, "onPackagesUnsuspended")) {
+                        if (!isEnabledProfileOf(cookie, user, "onPackagesUnsuspended")) {
                             continue;
                         }
                         final String[] filteredPackages =
@@ -2551,7 +2618,7 @@
                     for (int i = 0; i < n; i++) {
                         IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
                         BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
-                        if (!isEnabledProfileOf(cookie.user, user, "onShortcutChanged")) {
+                        if (!isEnabledProfileOf(cookie, user, "onShortcutChanged")) {
                             continue;
                         }
                         if (!isPackageVisibleToListener(packageName, cookie, user)) {
@@ -2625,7 +2692,7 @@
                     for (int i = 0; i < n; i++) {
                         IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
                         BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
-                        if (!isEnabledProfileOf(cookie.user, mUser, "onLoadingProgressChanged")) {
+                        if (!isEnabledProfileOf(cookie, mUser, "onLoadingProgressChanged")) {
                             continue;
                         }
                         if (!isPackageVisibleToListener(mPackageName, cookie, mUser)) {
diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java
index 05e8f9a..df4e5a3 100644
--- a/services/core/java/com/android/server/pm/PackageArchiver.java
+++ b/services/core/java/com/android/server/pm/PackageArchiver.java
@@ -354,21 +354,18 @@
                 ps.setArchiveState(/* archiveState= */ null, userId);
             }
         }
-        mPm.mBackgroundHandler.post(
-                () -> {
-                    File iconsDir = getIconsDir(packageName, userId);
-                    if (!iconsDir.exists()) {
-                        return;
-                    }
-                    // TODO(b/319238030) Move this into installd.
-                    if (!FileUtils.deleteContentsAndDir(iconsDir)) {
-                        Slog.e(TAG, "Failed to clean up archive files for " + packageName);
-                    } else {
-                        if (DEBUG) {
-                            Slog.e(TAG, "Deleted icons at " + iconsDir.getAbsolutePath());
-                        }
-                    }
-                });
+        File iconsDir = getIconsDir(packageName, userId);
+        if (!iconsDir.exists()) {
+            return;
+        }
+        // TODO(b/319238030) Move this into installd.
+        if (!FileUtils.deleteContentsAndDir(iconsDir)) {
+            Slog.e(TAG, "Failed to clean up archive files for " + packageName);
+        } else {
+            if (DEBUG) {
+                Slog.e(TAG, "Deleted icons at " + iconsDir.getAbsolutePath());
+            }
+        }
     }
 
     @Nullable
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index f222fe9..b505a7c 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -973,7 +973,7 @@
         mUsers = users != null ? users : new SparseArray<>();
         mHandler = new MainHandler();
         mInternalExecutor = new ThreadPoolExecutor(/* corePoolSize */ 0, /* maximumPoolSize */ 1,
-                /* keepAliveTime */ 1, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
+                /* keepAliveTime */ 24, TimeUnit.HOURS, new LinkedBlockingQueue<>());
         mUserVisibilityMediator = new UserVisibilityMediator(mHandler);
         mUserDataPreparer = userDataPreparer;
         mUserTypes = UserTypeFactory.getUserTypes();
diff --git a/services/core/java/com/android/server/search/Searchables.java b/services/core/java/com/android/server/search/Searchables.java
index 6e1e979..7b39775 100644
--- a/services/core/java/com/android/server/search/Searchables.java
+++ b/services/core/java/com/android/server/search/Searchables.java
@@ -147,6 +147,9 @@
             Log.e(LOG_TAG, "Error getting activity info " + re);
             return null;
         }
+        if (ai == null) {
+            return null;
+        }
         String refActivityName = null;
 
         // First look for activity-specific reference
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index fab0430..99ce3e2 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -31,6 +31,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
+import android.app.SynchronousUserSwitchObserver;
 import android.app.UidObserver;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -145,8 +146,6 @@
                     PowerManager.GO_TO_SLEEP_REASON_INATTENTIVE,
                     PowerManager.GO_TO_SLEEP_REASON_TIMEOUT));
 
-    private static final IntentFilter USER_SWITCHED_INTENT_FILTER =
-            new IntentFilter(Intent.ACTION_USER_SWITCHED);
     private static final IntentFilter INTERNAL_RINGER_MODE_CHANGED_INTENT_FILTER =
             new IntentFilter(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
 
@@ -162,9 +161,11 @@
     @VisibleForTesting
     final SettingsContentObserver mSettingObserver;
     @VisibleForTesting
-    final MyUidObserver mUidObserver;
-    @VisibleForTesting
     final SettingsBroadcastReceiver mSettingChangeReceiver;
+    @VisibleForTesting
+    final VibrationUidObserver mUidObserver;
+    @VisibleForTesting
+    final VibrationUserSwitchObserver mUserSwitchObserver;
 
     @GuardedBy("mLock")
     private final List<OnVibratorSettingsChanged> mListeners = new ArrayList<>();
@@ -205,8 +206,9 @@
         mContext = context;
         mVibrationConfig = config;
         mSettingObserver = new SettingsContentObserver(handler);
-        mUidObserver = new MyUidObserver();
         mSettingChangeReceiver = new SettingsBroadcastReceiver();
+        mUidObserver = new VibrationUidObserver();
+        mUserSwitchObserver = new VibrationUserSwitchObserver();
         mSystemUiPackage = LocalServices.getService(PackageManagerInternal.class)
                 .getSystemUiServiceComponent().getPackageName();
 
@@ -245,7 +247,13 @@
         try {
             ActivityManager.getService().registerUidObserver(mUidObserver,
                     ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE,
-                    ActivityManager.PROCESS_STATE_UNKNOWN, null);
+                    ActivityManager.PROCESS_STATE_UNKNOWN, /* callingPackage= */ null);
+        } catch (RemoteException e) {
+            // ignored; both services live in system_server
+        }
+
+        try {
+            ActivityManager.getService().registerUserSwitchObserver(mUserSwitchObserver, TAG);
         } catch (RemoteException e) {
             // ignored; both services live in system_server
         }
@@ -270,7 +278,6 @@
                     }
                 });
 
-        registerSettingsChangeReceiver(USER_SWITCHED_INTENT_FILTER);
         registerSettingsChangeReceiver(INTERNAL_RINGER_MODE_CHANGED_INTENT_FILTER);
 
         // Listen to all settings that might affect the result of Vibrator.getVibrationIntensity.
@@ -540,41 +547,44 @@
 
     /** Update all cached settings and triggers registered listeners. */
     void update() {
-        updateSettings();
+        updateSettings(UserHandle.USER_CURRENT);
         updateRingerMode();
         notifyListeners();
     }
 
-    private void updateSettings() {
+    private void updateSettings(int userHandle) {
         synchronized (mLock) {
-            mVibrateInputDevices = loadSystemSetting(Settings.System.VIBRATE_INPUT_DEVICES, 0) > 0;
-            mVibrateOn = loadSystemSetting(Settings.System.VIBRATE_ON, 1) > 0;
+            mVibrateInputDevices =
+                    loadSystemSetting(Settings.System.VIBRATE_INPUT_DEVICES, 0, userHandle) > 0;
+            mVibrateOn = loadSystemSetting(Settings.System.VIBRATE_ON, 1, userHandle) > 0;
             mKeyboardVibrationOn = loadSystemSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED,
-                    mVibrationConfig.isDefaultKeyboardVibrationEnabled() ? 1 : 0) > 0;
+                    mVibrationConfig.isDefaultKeyboardVibrationEnabled() ? 1 : 0, userHandle) > 0;
 
             int alarmIntensity = toIntensity(
-                    loadSystemSetting(Settings.System.ALARM_VIBRATION_INTENSITY, -1),
+                    loadSystemSetting(Settings.System.ALARM_VIBRATION_INTENSITY, -1, userHandle),
                     getDefaultIntensity(USAGE_ALARM));
             int defaultHapticFeedbackIntensity = getDefaultIntensity(USAGE_TOUCH);
             int hapticFeedbackIntensity = toIntensity(
-                    loadSystemSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, -1),
+                    loadSystemSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, -1, userHandle),
                     defaultHapticFeedbackIntensity);
             int positiveHapticFeedbackIntensity = toPositiveIntensity(
                     hapticFeedbackIntensity, defaultHapticFeedbackIntensity);
             int hardwareFeedbackIntensity = toIntensity(
-                    loadSystemSetting(Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, -1),
+                    loadSystemSetting(Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, -1,
+                            userHandle),
                     positiveHapticFeedbackIntensity);
             int mediaIntensity = toIntensity(
-                    loadSystemSetting(Settings.System.MEDIA_VIBRATION_INTENSITY, -1),
+                    loadSystemSetting(Settings.System.MEDIA_VIBRATION_INTENSITY, -1, userHandle),
                     getDefaultIntensity(USAGE_MEDIA));
             int defaultNotificationIntensity = getDefaultIntensity(USAGE_NOTIFICATION);
             int notificationIntensity = toIntensity(
-                    loadSystemSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, -1),
+                    loadSystemSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, -1,
+                            userHandle),
                     defaultNotificationIntensity);
             int positiveNotificationIntensity = toPositiveIntensity(
                     notificationIntensity, defaultNotificationIntensity);
             int ringIntensity = toIntensity(
-                    loadSystemSetting(Settings.System.RING_VIBRATION_INTENSITY, -1),
+                    loadSystemSetting(Settings.System.RING_VIBRATION_INTENSITY, -1, userHandle),
                     getDefaultIntensity(USAGE_RINGTONE));
 
             mCurrentVibrationIntensities.clear();
@@ -593,7 +603,7 @@
             mCurrentVibrationIntensities.put(USAGE_HARDWARE_FEEDBACK, hardwareFeedbackIntensity);
             mCurrentVibrationIntensities.put(USAGE_PHYSICAL_EMULATION, hardwareFeedbackIntensity);
 
-            if (!loadBooleanSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED)) {
+            if (!loadBooleanSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED, userHandle)) {
                 // Make sure deprecated boolean setting still disables touch vibrations.
                 mCurrentVibrationIntensities.put(USAGE_TOUCH, Vibrator.VIBRATION_INTENSITY_OFF);
             } else {
@@ -744,14 +754,13 @@
         return value;
     }
 
-    private boolean loadBooleanSetting(String settingKey) {
-        return Settings.System.getIntForUser(mContext.getContentResolver(),
-                settingKey, 0, UserHandle.USER_CURRENT) != 0;
+    private boolean loadBooleanSetting(String settingKey, int userHandle) {
+        return loadSystemSetting(settingKey, 0, userHandle) != 0;
     }
 
-    private int loadSystemSetting(String settingName, int defaultValue) {
+    private int loadSystemSetting(String settingName, int defaultValue, int userHandle) {
         return Settings.System.getIntForUser(mContext.getContentResolver(),
-                settingName, defaultValue, UserHandle.USER_CURRENT);
+                settingName, defaultValue, userHandle);
     }
 
     private void registerSettingsObserver(Uri settingUri) {
@@ -828,24 +837,18 @@
 
         @Override
         public void onChange(boolean selfChange) {
-            updateSettings();
+            updateSettings(UserHandle.USER_CURRENT);
             notifyListeners();
         }
     }
 
-    /**
-     * Implementation of {@link BroadcastReceiver} to update settings on current user or ringer
-     * mode change.
-     */
+    /** Implementation of {@link BroadcastReceiver} to update on ringer mode change. */
     @VisibleForTesting
     final class SettingsBroadcastReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
-            if (Intent.ACTION_USER_SWITCHED.equals(action)) {
-                // Reload all settings, as they are user-based.
-                update();
-            } else if (AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION.equals(action)) {
+            if (AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION.equals(action)) {
                 updateRingerMode();
                 notifyListeners();
             }
@@ -854,7 +857,7 @@
 
     /** Implementation of {@link ContentObserver} to be registered to a setting {@link Uri}. */
     @VisibleForTesting
-    final class MyUidObserver extends UidObserver {
+    final class VibrationUidObserver extends UidObserver {
         private final SparseArray<Integer> mProcStatesCache = new SparseArray<>();
 
         public boolean isUidForeground(int uid) {
@@ -878,4 +881,23 @@
             }
         }
     }
+
+    /** Implementation of {@link SynchronousUserSwitchObserver} to update on user switch. */
+    @VisibleForTesting
+    final class VibrationUserSwitchObserver extends SynchronousUserSwitchObserver {
+
+        @Override
+        public void onUserSwitching(int newUserId) {
+            // Reload settings early based on new user id.
+            updateSettings(newUserId);
+            notifyListeners();
+        }
+
+        @Override
+        public void onUserSwitchComplete(int newUserId) {
+            // Reload all settings including ones from AudioManager,
+            // as they are based on UserHandle.USER_CURRENT.
+            update();
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 759450b..5d172a9 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -2220,9 +2220,12 @@
             // only cancel background vibrations.
             IBinder deathBinder = commonOptions.background ? VibratorManagerService.this
                     : mShellCallbacksToken;
-            HalVibration vib = vibrateWithPermissionCheck(Binder.getCallingUid(),
-                    Context.DEVICE_ID_DEFAULT, SHELL_PACKAGE_NAME, combined, attrs,
-                    commonOptions.description, deathBinder);
+            int uid = Binder.getCallingUid();
+            // Resolve the package name for the client based on the process UID, to cover cases like
+            // rooted shell clients using ROOT_UID.
+            String resolvedPackageName = AppOpsManager.resolvePackageName(uid, SHELL_PACKAGE_NAME);
+            HalVibration vib = vibrateWithPermissionCheck(uid, Context.DEVICE_ID_DEFAULT,
+                    resolvedPackageName, combined, attrs, commonOptions.description, deathBinder);
             maybeWaitOnVibration(vib, commonOptions);
         }
 
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 118985a..18afafd 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -52,6 +52,7 @@
 import android.app.ILocalWallpaperColorConsumer;
 import android.app.IWallpaperManager;
 import android.app.IWallpaperManagerCallback;
+import android.app.KeyguardManager;
 import android.app.PendingIntent;
 import android.app.UidObserver;
 import android.app.UserSwitchObserver;
@@ -1792,10 +1793,27 @@
                     systemWallpaper.wallpaperObserver = new WallpaperObserver(systemWallpaper);
                     systemWallpaper.wallpaperObserver.startWatching();
                 }
-                if (lockWallpaper != systemWallpaper)  {
-                    switchWallpaper(lockWallpaper, null);
+                if (Flags.reorderWallpaperDuringUserSwitch()) {
+                    if (mLastLockWallpaper != null) {
+                        detachWallpaperLocked(mLastLockWallpaper);
+                    }
+                    if (mLastWallpaper != null) {
+                        detachWallpaperLocked(mLastWallpaper);
+                    }
+                    if (lockWallpaper == systemWallpaper)  {
+                        switchWallpaper(systemWallpaper, reply);
+                    } else {
+                        KeyguardManager km = mContext.getSystemService(KeyguardManager.class);
+                        boolean isDeviceSecure = km != null && km.isDeviceSecure(userId);
+                        switchWallpaper(isDeviceSecure ? lockWallpaper : systemWallpaper, reply);
+                        switchWallpaper(isDeviceSecure ? systemWallpaper : lockWallpaper, null);
+                    }
+                } else {
+                    if (lockWallpaper != systemWallpaper)  {
+                        switchWallpaper(lockWallpaper, null);
+                    }
+                    switchWallpaper(systemWallpaper, reply);
                 }
-                switchWallpaper(systemWallpaper, reply);
                 mInitialUserSwitch = false;
             }
 
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/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 5243527..5d613cf 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -52,7 +52,6 @@
 import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_SPLIT_SCREEN;
 import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_UNSET;
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
-import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
 import static android.content.res.Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
 import static android.content.res.Configuration.SCREEN_WIDTH_DP_UNDEFINED;
@@ -1369,23 +1368,25 @@
      *
      * <p>Conditions that needs to be met:
      * <ul>
-     *   <li>Activity is portrait-only.
-     *   <li>Fullscreen window in landscape device orientation.
+     *   <li>Windowing mode is fullscreen.
      *   <li>Horizontal Reachability is enabled.
-     *   <li>Activity fills parent vertically.
+     *   <li>First top opaque activity fills parent vertically, but not horizontally.
      * </ul>
      */
     private boolean isHorizontalReachabilityEnabled(Configuration parentConfiguration) {
         // Use screen resolved bounds which uses resolved bounds or size compat bounds
         // as activity bounds can sometimes be empty
+        final Rect opaqueActivityBounds = hasInheritedLetterboxBehavior()
+                ? mFirstOpaqueActivityBeneath.getScreenResolvedBounds()
+                : mActivityRecord.getScreenResolvedBounds();
         return mLetterboxConfiguration.getIsHorizontalReachabilityEnabled()
                 && parentConfiguration.windowConfiguration.getWindowingMode()
                         == WINDOWING_MODE_FULLSCREEN
-                && (parentConfiguration.orientation == ORIENTATION_LANDSCAPE
-                        && mActivityRecord.getOrientationForReachability() == ORIENTATION_PORTRAIT)
                 // Check whether the activity fills the parent vertically.
                 && parentConfiguration.windowConfiguration.getAppBounds().height()
-                        <= mActivityRecord.getScreenResolvedBounds().height();
+                        <= opaqueActivityBounds.height()
+                && parentConfiguration.windowConfiguration.getAppBounds().width()
+                        > opaqueActivityBounds.width();
     }
 
     @VisibleForTesting
@@ -1402,23 +1403,25 @@
      *
      * <p>Conditions that needs to be met:
      * <ul>
-     *   <li>Activity is landscape-only.
-     *   <li>Fullscreen window in portrait device orientation.
+     *   <li>Windowing mode is fullscreen.
      *   <li>Vertical Reachability is enabled.
-     *   <li>Activity fills parent horizontally.
+     *   <li>First top opaque activity fills parent horizontally but not vertically.
      * </ul>
      */
     private boolean isVerticalReachabilityEnabled(Configuration parentConfiguration) {
         // Use screen resolved bounds which uses resolved bounds or size compat bounds
         // as activity bounds can sometimes be empty
+        final Rect opaqueActivityBounds = hasInheritedLetterboxBehavior()
+                ? mFirstOpaqueActivityBeneath.getScreenResolvedBounds()
+                : mActivityRecord.getScreenResolvedBounds();
         return mLetterboxConfiguration.getIsVerticalReachabilityEnabled()
                 && parentConfiguration.windowConfiguration.getWindowingMode()
                         == WINDOWING_MODE_FULLSCREEN
-                && (parentConfiguration.orientation == ORIENTATION_PORTRAIT
-                        && mActivityRecord.getOrientationForReachability() == ORIENTATION_LANDSCAPE)
                 // Check whether the activity fills the parent horizontally.
-                && parentConfiguration.windowConfiguration.getBounds().width()
-                        == mActivityRecord.getScreenResolvedBounds().width();
+                && parentConfiguration.windowConfiguration.getAppBounds().width()
+                        <= opaqueActivityBounds.width()
+                && parentConfiguration.windowConfiguration.getAppBounds().height()
+                        > opaqueActivityBounds.height();
     }
 
     @VisibleForTesting
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/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 70775530..503f925 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -267,7 +267,8 @@
         mSyncEngine.addOnIdleListener(this::tryStartCollectFromQueue);
     }
 
-    private void detachPlayer() {
+    @VisibleForTesting
+    void detachPlayer() {
         if (mTransitionPlayer == null) return;
         // Immediately set to null so that nothing inadvertently starts/queues.
         mTransitionPlayer = null;
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/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java
index 1ae6e63..0d826df 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java
@@ -43,8 +43,10 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.InstallSourceInfo;
 import android.content.pm.PackageInfo;
+import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
+import android.content.pm.ParceledListSlice;
 import android.os.FileUtils;
 import android.os.Looper;
 import android.os.RemoteException;
@@ -115,9 +117,6 @@
     private BackgroundInstallControlCallbackHelper mCallbackHelper;
 
     @Captor
-    private ArgumentCaptor<PackageManagerInternal.PackageListObserver> mPackageListObserverCaptor;
-
-    @Captor
     private ArgumentCaptor<UsageEventListener> mUsageEventListenerCaptor;
 
     @Before
@@ -137,8 +136,8 @@
         mUsageEventListener = mUsageEventListenerCaptor.getValue();
 
         mBackgroundInstallControlService.onStart(true);
-        verify(mPackageManagerInternal).getPackageList(mPackageListObserverCaptor.capture());
-        mPackageListObserver = mPackageListObserverCaptor.getValue();
+
+        mPackageListObserver = mBackgroundInstallControlService.mPackageObserver;
     }
 
     @After
@@ -554,6 +553,7 @@
         assertEquals(0, foregroundTimeFrames.size());
     }
 
+    //package installed, but no UI interaction found
     @Test
     public void testHandleUsageEvent_packageAddedNoUsageEvent()
             throws NoSuchFieldException, PackageManager.NameNotFoundException {
@@ -571,12 +571,10 @@
         when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt()))
                 .thenReturn(appInfo);
 
-        long createTimestamp =
-                PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis());
         FieldSetter.setField(
                 appInfo,
                 ApplicationInfo.class.getDeclaredField("createTimestamp"),
-                createTimestamp);
+                convertToTestAdjustTimestamp(PACKAGE_ADD_TIMESTAMP_1));
 
         int uid = USER_ID_1 * UserHandle.PER_USER_RANGE;
         assertEquals(USER_ID_1, UserHandle.getUserId(uid));
@@ -590,6 +588,10 @@
         assertTrue(packages.contains(USER_ID_1, PACKAGE_NAME_1));
     }
 
+    private long convertToTestAdjustTimestamp(long timestamp) {
+        return timestamp - (System.currentTimeMillis() - SystemClock.uptimeMillis());
+    }
+
     @Test
     public void testHandleUsageEvent_packageAddedInsideTimeFrame()
             throws NoSuchFieldException, PackageManager.NameNotFoundException {
@@ -607,12 +609,10 @@
         when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt()))
                 .thenReturn(appInfo);
 
-        long createTimestamp =
-                PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis());
         FieldSetter.setField(
                 appInfo,
                 ApplicationInfo.class.getDeclaredField("createTimestamp"),
-                createTimestamp);
+                convertToTestAdjustTimestamp(PACKAGE_ADD_TIMESTAMP_1));
 
         int uid = USER_ID_1 * UserHandle.PER_USER_RANGE;
         assertEquals(USER_ID_1, UserHandle.getUserId(uid));
@@ -639,6 +639,122 @@
     }
 
     @Test
+    public void testHandleUsageEvent_fallsBackToAppInfoTimeWhenHistoricalSessionsNotFound()
+            throws NoSuchFieldException, PackageManager.NameNotFoundException {
+        assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages());
+        InstallSourceInfo installSourceInfo =
+                new InstallSourceInfo(
+                        /* initiatingPackageName= */ INSTALLER_NAME_1,
+                        /* initiatingPackageSigningInfo= */ null,
+                        /* originatingPackageName= */ null,
+                        /* installingPackageName= */ INSTALLER_NAME_1);
+        assertEquals(installSourceInfo.getInstallingPackageName(), INSTALLER_NAME_1);
+        when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(installSourceInfo);
+        ApplicationInfo appInfo = mock(ApplicationInfo.class);
+
+        when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt()))
+                .thenReturn(appInfo);
+
+        FieldSetter.setField(
+                appInfo,
+                ApplicationInfo.class.getDeclaredField("createTimestamp"),
+                // create timestamp is after generated foreground events (hence not considered
+                // foreground install)
+                convertToTestAdjustTimestamp(USAGE_EVENT_TIMESTAMP_2 + 1));
+
+        int uid = USER_ID_1 * UserHandle.PER_USER_RANGE;
+        assertEquals(USER_ID_1, UserHandle.getUserId(uid));
+
+        createPackageManagerHistoricalSessions(List.of(), USER_ID_1);
+
+        // The 2 relevants usage events are before the timeframe, the app is not considered
+        // foreground installed.
+        doReturn(PERMISSION_GRANTED)
+                .when(mPermissionManager)
+                .checkPermission(anyString(), anyString(), anyString(), anyInt());
+        generateUsageEvent(
+                UsageEvents.Event.ACTIVITY_RESUMED,
+                USER_ID_1,
+                INSTALLER_NAME_1,
+                USAGE_EVENT_TIMESTAMP_1);
+        generateUsageEvent(
+                Event.ACTIVITY_STOPPED, USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2);
+
+        mPackageListObserver.onPackageAdded(PACKAGE_NAME_1, uid);
+        mTestLooper.dispatchAll();
+
+        var packages = mBackgroundInstallControlService.getBackgroundInstalledPackages();
+        assertNotNull(packages);
+        assertEquals(1, packages.size());
+        assertTrue(packages.contains(USER_ID_1, PACKAGE_NAME_1));
+    }
+
+    @Test
+    public void testHandleUsageEvent_usesHistoricalSessionCreateTimeWhenHistoricalSessionsFound()
+            throws NoSuchFieldException, PackageManager.NameNotFoundException {
+        assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages());
+        InstallSourceInfo installSourceInfo =
+                new InstallSourceInfo(
+                        /* initiatingPackageName= */ INSTALLER_NAME_1,
+                        /* initiatingPackageSigningInfo= */ null,
+                        /* originatingPackageName= */ null,
+                        /* installingPackageName= */ INSTALLER_NAME_1);
+        assertEquals(installSourceInfo.getInstallingPackageName(), INSTALLER_NAME_1);
+        when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(installSourceInfo);
+        ApplicationInfo appInfo = mock(ApplicationInfo.class);
+
+        when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt()))
+                .thenReturn(appInfo);
+
+        FieldSetter.setField(
+                appInfo,
+                ApplicationInfo.class.getDeclaredField("createTimestamp"),
+                //create timestamp is out of window of (after) the interact events
+                convertToTestAdjustTimestamp(USAGE_EVENT_TIMESTAMP_2 + 1));
+
+        int uid = USER_ID_1 * UserHandle.PER_USER_RANGE;
+        assertEquals(USER_ID_1, UserHandle.getUserId(uid));
+
+        PackageInstaller.SessionInfo installSession1 = mock(PackageInstaller.SessionInfo.class);
+        PackageInstaller.SessionInfo installSession2 = mock(PackageInstaller.SessionInfo.class);
+        doReturn(convertToTestAdjustTimestamp(0L)).when(installSession1).getCreatedMillis();
+        doReturn(convertToTestAdjustTimestamp(PACKAGE_ADD_TIMESTAMP_1)).when(installSession2)
+                .getCreatedMillis();
+        doReturn(PACKAGE_NAME_1).when(installSession1).getAppPackageName();
+        doReturn(PACKAGE_NAME_1).when(installSession2).getAppPackageName();
+        createPackageManagerHistoricalSessions(List.of(installSession1, installSession2),
+                USER_ID_1);
+
+        // The following 2 generated usage events occur after historical session create times hence,
+        // considered foreground install. The appInfo createTimestamp occurs after events, so the
+        // app would be considered background install if it falls back to it as reference create
+        // timestamp.
+        doReturn(PERMISSION_GRANTED)
+                .when(mPermissionManager)
+                .checkPermission(anyString(), anyString(), anyString(), anyInt());
+        generateUsageEvent(
+                UsageEvents.Event.ACTIVITY_RESUMED,
+                USER_ID_1,
+                INSTALLER_NAME_1,
+                USAGE_EVENT_TIMESTAMP_1);
+        generateUsageEvent(
+                Event.ACTIVITY_STOPPED, USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2);
+
+        mPackageListObserver.onPackageAdded(PACKAGE_NAME_1, uid);
+        mTestLooper.dispatchAll();
+
+        assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages());
+    }
+
+    private void createPackageManagerHistoricalSessions(
+            List<PackageInstaller.SessionInfo> sessions, int userId) {
+        ParceledListSlice<PackageInstaller.SessionInfo> mockParcelList =
+                mock(ParceledListSlice.class);
+        when(mockParcelList.getList()).thenReturn(sessions);
+        when(mPackageManagerInternal.getHistoricalSessions(userId)).thenReturn(mockParcelList);
+    }
+
+    @Test
     public void testHandleUsageEvent_packageAddedOutsideTimeFrame1()
             throws NoSuchFieldException, PackageManager.NameNotFoundException {
         assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages());
@@ -655,12 +771,10 @@
         when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt()))
                 .thenReturn(appInfo);
 
-        long createTimestamp =
-                PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis());
         FieldSetter.setField(
                 appInfo,
                 ApplicationInfo.class.getDeclaredField("createTimestamp"),
-                createTimestamp);
+                convertToTestAdjustTimestamp(PACKAGE_ADD_TIMESTAMP_1));
 
         int uid = USER_ID_1 * UserHandle.PER_USER_RANGE;
         assertEquals(USER_ID_1, UserHandle.getUserId(uid));
@@ -708,12 +822,10 @@
         when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt()))
                 .thenReturn(appInfo);
 
-        long createTimestamp =
-                PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis());
         FieldSetter.setField(
                 appInfo,
                 ApplicationInfo.class.getDeclaredField("createTimestamp"),
-                createTimestamp);
+                convertToTestAdjustTimestamp(PACKAGE_ADD_TIMESTAMP_1));
 
         int uid = USER_ID_1 * UserHandle.PER_USER_RANGE;
         assertEquals(USER_ID_1, UserHandle.getUserId(uid));
@@ -765,12 +877,10 @@
         when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt()))
                 .thenReturn(appInfo);
 
-        long createTimestamp =
-                PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis());
         FieldSetter.setField(
                 appInfo,
                 ApplicationInfo.class.getDeclaredField("createTimestamp"),
-                createTimestamp);
+                convertToTestAdjustTimestamp(PACKAGE_ADD_TIMESTAMP_1));
 
         int uid = USER_ID_1 * UserHandle.PER_USER_RANGE;
         assertEquals(USER_ID_1, UserHandle.getUserId(uid));
@@ -818,12 +928,10 @@
         when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt()))
                 .thenReturn(appInfo);
 
-        long createTimestamp =
-                PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis());
         FieldSetter.setField(
                 appInfo,
                 ApplicationInfo.class.getDeclaredField("createTimestamp"),
-                createTimestamp);
+                convertToTestAdjustTimestamp(PACKAGE_ADD_TIMESTAMP_1));
 
         int uid = USER_ID_1 * UserHandle.PER_USER_RANGE;
         assertEquals(USER_ID_1, UserHandle.getUserId(uid));
diff --git a/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java b/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java
index 8bccce1..f5c6795 100644
--- a/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java
+++ b/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java
@@ -16,41 +16,60 @@
 
 package com.android.server.search;
 
-import android.app.SearchManager;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+
 import android.app.SearchableInfo;
 import android.app.SearchableInfo.ActionKeyInfo;
 import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ProviderInfo;
-import android.content.pm.ResolveInfo;
-import android.content.res.Resources;
-import android.content.res.XmlResourceParser;
-import android.os.RemoteException;
-import com.android.server.search.Searchables;
-import android.test.AndroidTestCase;
+import android.content.pm.PackageManagerInternal;
 import android.test.MoreAsserts;
-import android.test.mock.MockContext;
-import android.test.mock.MockPackageManager;
-import android.test.suitebuilder.annotation.SmallTest;
 import android.view.KeyEvent;
 
-import java.util.ArrayList;
-import java.util.List;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
-/**
- * To launch this test from the command line:
- * 
- * adb shell am instrument -w \
- *   -e class com.android.unit_tests.SearchablesTest \
- *   com.android.unit_tests/android.test.InstrumentationTestRunner
- */
+import com.android.server.LocalServices;
+
+import org.junit.After;
+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;
+
+@RunWith(AndroidJUnit4.class)
 @SmallTest
-public class SearchablesTest extends AndroidTestCase {
-    
+public class SearchablesTest {
+    @Mock protected PackageManagerInternal mPackageManagerInternal;
+
+    private Context mContext;
+
+    @Before
+    public final void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        LocalServices.removeServiceForTest(PackageManagerInternal.class);
+        LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal);
+
+        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+    }
+
+    @After
+    public final void tearDown() {
+        Mockito.framework().clearInlineMocks();
+    }
+
     /*
      * SearchableInfo tests
      *  Mock the context so I can provide very specific input data
@@ -69,15 +88,15 @@
      * Test that non-searchable activities return no searchable info (this would typically
      * trigger the use of the default searchable e.g. contacts)
      */
+    @Test
     public void testNonSearchable() {
         // test basic array & hashmap
         Searchables searchables = new Searchables(mContext, 0);
         searchables.updateSearchableList();
 
         // confirm that we return null for non-searchy activities
-        ComponentName nonActivity = new ComponentName(
-                            "com.android.frameworks.coretests",
-                            "com.android.frameworks.coretests.activity.NO_SEARCH_ACTIVITY");
+        ComponentName nonActivity = new ComponentName("com.android.frameworks.servicestests",
+                "com.android.frameworks.servicestests.activity.NO_SEARCH_ACTIVITY");
         SearchableInfo si = searchables.getSearchableInfo(nonActivity);
         assertNull(si);
     }
@@ -97,13 +116,11 @@
      *  getIcon works
 
      */
+    @Test
     public void testSearchablesListReal() {
-        MyMockPackageManager mockPM = new MyMockPackageManager(mContext.getPackageManager());
-        MyMockContext mockContext = new MyMockContext(mContext, mockPM);
+        doReturn(true).when(mPackageManagerInternal).canAccessComponent(anyInt(), any(), anyInt());
 
-        // build item list with real-world source data
-        mockPM.setSearchablesMode(MyMockPackageManager.SEARCHABLES_PASSTHROUGH);
-        Searchables searchables = new Searchables(mockContext, 0);
+        Searchables searchables = new Searchables(mContext, 0);
         searchables.updateSearchableList();
         // tests with "real" searchables (deprecate, this should be a unit test)
         ArrayList<SearchableInfo> searchablesList = searchables.getSearchablesList();
@@ -117,12 +134,11 @@
     /**
      * This round of tests confirms good operations with "zero" searchables found
      */
+    @Test
     public void testSearchablesListEmpty() {
-        MyMockPackageManager mockPM = new MyMockPackageManager(mContext.getPackageManager());
-        MyMockContext mockContext = new MyMockContext(mContext, mockPM);
+        doReturn(false).when(mPackageManagerInternal).canAccessComponent(anyInt(), any(), anyInt());
 
-        mockPM.setSearchablesMode(MyMockPackageManager.SEARCHABLES_MOCK_ZERO);
-        Searchables searchables = new Searchables(mockContext, 0);
+        Searchables searchables = new Searchables(mContext, 0);
         searchables.updateSearchableList();
         ArrayList<SearchableInfo> searchablesList = searchables.getSearchablesList();
         assertNotNull(searchablesList);
@@ -219,231 +235,6 @@
         if (s != null) {
             MoreAsserts.assertNotEqual(s, "");
         }
-    }    
-    
-    /**
-     * This is a mock for context.  Used to perform a true unit test on SearchableInfo.
-     * 
-     */
-    private class MyMockContext extends MockContext {
-        
-        protected Context mRealContext;
-        protected PackageManager mPackageManager;
-        
-        /**
-         * Constructor.
-         * 
-         * @param realContext Please pass in a real context for some pass-throughs to function.
-         */
-        MyMockContext(Context realContext, PackageManager packageManager) {
-            mRealContext = realContext;
-            mPackageManager = packageManager;
-        }
-        
-        /**
-         * Resources.  Pass through for now.
-         */
-        @Override
-        public Resources getResources() {
-            return mRealContext.getResources();
-        }
-
-        /**
-         * Package manager.  Pass through for now.
-         */
-        @Override
-        public PackageManager getPackageManager() {
-            return mPackageManager;
-        }
-
-        /**
-         * Package manager.  Pass through for now.
-         */
-        @Override
-        public Context createPackageContext(String packageName, int flags)
-                throws PackageManager.NameNotFoundException {
-            return mRealContext.createPackageContext(packageName, flags);
-        }
-
-        /**
-         * Message broadcast.  Pass through for now.
-         */
-        @Override
-        public void sendBroadcast(Intent intent) {
-            mRealContext.sendBroadcast(intent);
-        }
-    }
-
-/**
- * This is a mock for package manager.  Used to perform a true unit test on SearchableInfo.
- * 
- */
-    private class MyMockPackageManager extends MockPackageManager {
-        
-        public final static int SEARCHABLES_PASSTHROUGH = 0;
-        public final static int SEARCHABLES_MOCK_ZERO = 1;
-        public final static int SEARCHABLES_MOCK_ONEGOOD = 2;
-        public final static int SEARCHABLES_MOCK_ONEGOOD_ONEBAD = 3;
-        
-        protected PackageManager mRealPackageManager;
-        protected int mSearchablesMode;
-
-        public MyMockPackageManager(PackageManager realPM) {
-            mRealPackageManager = realPM;
-            mSearchablesMode = SEARCHABLES_PASSTHROUGH;
-        }
-
-        /**
-         * Set the mode for various tests.
-         */
-        public void setSearchablesMode(int newMode) {
-            switch (newMode) {
-            case SEARCHABLES_PASSTHROUGH:
-            case SEARCHABLES_MOCK_ZERO:
-                mSearchablesMode = newMode;
-                break;
-                
-            default:
-                throw new UnsupportedOperationException();       
-            }
-        }
-        
-        /**
-         * Find activities that support a given intent.
-         * 
-         * Retrieve all activities that can be performed for the given intent.
-         * 
-         * @param intent The desired intent as per resolveActivity().
-         * @param flags Additional option flags.  The most important is
-         *                    MATCH_DEFAULT_ONLY, to limit the resolution to only
-         *                    those activities that support the CATEGORY_DEFAULT.
-         * 
-         * @return A List<ResolveInfo> containing one entry for each matching
-         *         Activity. These are ordered from best to worst match -- that
-         *         is, the first item in the list is what is returned by
-         *         resolveActivity().  If there are no matching activities, an empty
-         *         list is returned.
-         */
-        @Override 
-        public List<ResolveInfo> queryIntentActivities(Intent intent, int flags) {
-            assertNotNull(intent);
-            assertTrue(intent.getAction().equals(Intent.ACTION_SEARCH)
-                    || intent.getAction().equals(Intent.ACTION_WEB_SEARCH)
-                    || intent.getAction().equals(SearchManager.INTENT_ACTION_GLOBAL_SEARCH));
-            switch (mSearchablesMode) {
-            case SEARCHABLES_PASSTHROUGH:
-                return mRealPackageManager.queryIntentActivities(intent, flags);
-            case SEARCHABLES_MOCK_ZERO:
-                return null;
-            default:
-                throw new UnsupportedOperationException();
-            }
-        }
-        
-        @Override
-        public ResolveInfo resolveActivity(Intent intent, int flags) {
-            assertNotNull(intent);
-            assertTrue(intent.getAction().equals(Intent.ACTION_WEB_SEARCH)
-                    || intent.getAction().equals(SearchManager.INTENT_ACTION_GLOBAL_SEARCH));
-            switch (mSearchablesMode) {
-            case SEARCHABLES_PASSTHROUGH:
-                return mRealPackageManager.resolveActivity(intent, flags);
-            case SEARCHABLES_MOCK_ZERO:
-                return null;
-            default:
-                throw new UnsupportedOperationException();
-            }
-        }
-
-        /**
-         * Retrieve an XML file from a package.  This is a low-level API used to
-         * retrieve XML meta data.
-         * 
-         * @param packageName The name of the package that this xml is coming from.
-         * Can not be null.
-         * @param resid The resource identifier of the desired xml.  Can not be 0.
-         * @param appInfo Overall information about <var>packageName</var>.  This
-         * may be null, in which case the application information will be retrieved
-         * for you if needed; if you already have this information around, it can
-         * be much more efficient to supply it here.
-         * 
-         * @return Returns an TypedXmlPullParser allowing you to parse out the XML
-         * data.  Returns null if the xml resource could not be found for any
-         * reason.
-         */
-        @Override 
-        public XmlResourceParser getXml(String packageName, int resid, ApplicationInfo appInfo) {
-            assertNotNull(packageName);
-            MoreAsserts.assertNotEqual(packageName, "");
-            MoreAsserts.assertNotEqual(resid, 0);
-            switch (mSearchablesMode) {
-            case SEARCHABLES_PASSTHROUGH:
-                return mRealPackageManager.getXml(packageName, resid, appInfo);
-            case SEARCHABLES_MOCK_ZERO:
-            default:
-                throw new UnsupportedOperationException();
-            }
-        }
-        
-        /**
-         * Find a single content provider by its base path name.
-         * 
-         * @param name The name of the provider to find.
-         * @param flags Additional option flags.  Currently should always be 0.
-         * 
-         * @return ContentProviderInfo Information about the provider, if found,
-         *         else null.
-         */
-        @Override 
-        public ProviderInfo resolveContentProvider(String name, int flags) {
-            assertNotNull(name);
-            MoreAsserts.assertNotEqual(name, "");
-            assertEquals(flags, 0);
-            switch (mSearchablesMode) {
-            case SEARCHABLES_PASSTHROUGH:
-                return mRealPackageManager.resolveContentProvider(name, flags);
-            case SEARCHABLES_MOCK_ZERO:
-            default:
-                throw new UnsupportedOperationException();
-            }
-        }
-
-        /**
-         * Get the activity information for a particular activity.
-         *
-         * @param name The name of the activity to find.
-         * @param flags Additional option flags.
-         *
-         * @return ActivityInfo Information about the activity, if found, else null.
-         */
-        @Override
-        public ActivityInfo getActivityInfo(ComponentName name, int flags)
-                throws NameNotFoundException {
-            assertNotNull(name);
-            MoreAsserts.assertNotEqual(name, "");
-            switch (mSearchablesMode) {
-            case SEARCHABLES_PASSTHROUGH:
-                return mRealPackageManager.getActivityInfo(name, flags);
-            case SEARCHABLES_MOCK_ZERO:
-                throw new NameNotFoundException();
-            default:
-                throw new UnsupportedOperationException();
-            }
-        }
-
-        @Override
-        public int checkPermission(String permName, String pkgName) {
-            assertNotNull(permName);
-            assertNotNull(pkgName);
-            switch (mSearchablesMode) {
-                case SEARCHABLES_PASSTHROUGH:
-                    return mRealPackageManager.checkPermission(permName, pkgName);
-                case SEARCHABLES_MOCK_ZERO:
-                    return PackageManager.PERMISSION_DENIED;
-                default:
-                    throw new UnsupportedOperationException();
-                }
-        }
     }
 }
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 6e5c180..3f2ccaf 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -5384,6 +5384,7 @@
         ZenRule rule = new ZenRule();
         rule.pkg = pkg;
         rule.creationTime = createdAt.toEpochMilli();
+        rule.enabled = true;
         rule.deletionInstant = deletedAt;
         // Plus stuff so that isValidAutomaticRule() passes
         rule.name = "A rule from " + pkg + " created on " + createdAt;
@@ -5392,6 +5393,47 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void getAutomaticZenRuleState_ownedRule_returnsRuleState() {
+        String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+                new AutomaticZenRule.Builder("Rule", CONDITION_ID)
+                        .setConfigurationActivity(
+                                new ComponentName(mContext.getPackageName(), "Blah"))
+                        .build(),
+                UPDATE_ORIGIN_APP, "reasons", CUSTOM_PKG_UID);
+
+        // Null condition -> STATE_FALSE
+        assertThat(mZenModeHelper.getAutomaticZenRuleState(id)).isEqualTo(Condition.STATE_FALSE);
+
+        mZenModeHelper.setAutomaticZenRuleState(id, CONDITION_TRUE, UPDATE_ORIGIN_APP,
+                CUSTOM_PKG_UID);
+        assertThat(mZenModeHelper.getAutomaticZenRuleState(id)).isEqualTo(Condition.STATE_TRUE);
+
+        mZenModeHelper.setAutomaticZenRuleState(id, CONDITION_FALSE, UPDATE_ORIGIN_APP,
+                CUSTOM_PKG_UID);
+        assertThat(mZenModeHelper.getAutomaticZenRuleState(id)).isEqualTo(Condition.STATE_FALSE);
+
+        mZenModeHelper.removeAutomaticZenRule(id, UPDATE_ORIGIN_APP, "", CUSTOM_PKG_UID);
+        assertThat(mZenModeHelper.getAutomaticZenRuleState(id)).isEqualTo(Condition.STATE_UNKNOWN);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void getAutomaticZenRuleState_notOwnedRule_returnsStateUnknown() {
+        // Assume existence of a system-owned rule that is currently ACTIVE.
+        ZenRule systemRule = newZenRule("android", Instant.now(), null);
+        systemRule.zenMode = ZEN_MODE_ALARMS;
+        systemRule.condition = new Condition(systemRule.conditionId, "on", Condition.STATE_TRUE);
+        ZenModeConfig config = mZenModeHelper.mConfig.copy();
+        config.automaticRules.put("systemRule", systemRule);
+        mZenModeHelper.setConfig(config, null, UPDATE_ORIGIN_INIT, "", Process.SYSTEM_UID);
+        assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_ALARMS);
+
+        assertThat(mZenModeHelper.getAutomaticZenRuleState("systemRule")).isEqualTo(
+                Condition.STATE_UNKNOWN);
+    }
+
+    @Test
     @EnableFlags(android.app.Flags.FLAG_MODES_API)
     public void testCallbacks_policy() throws Exception {
         setupZenConfig();
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
index f080341..f54c7e5 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
@@ -100,6 +100,8 @@
     @Rule
     public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
 
+    private static final int OLD_USER_ID = 123;
+    private static final int NEW_USER_ID = 456;
     private static final int UID = 1;
     private static final int VIRTUAL_DEVICE_ID = 1;
     private static final String SYSUI_PACKAGE_NAME = "sysui";
@@ -211,10 +213,10 @@
         mVibrationSettings.addListener(mListenerMock);
 
         // Testing the broadcast flow manually.
-        mVibrationSettings.mSettingChangeReceiver.onReceive(mContextSpy,
-                new Intent(Intent.ACTION_USER_SWITCHED));
+        mVibrationSettings.mUserSwitchObserver.onUserSwitching(NEW_USER_ID);
+        mVibrationSettings.mUserSwitchObserver.onUserSwitchComplete(NEW_USER_ID);
 
-        verify(mListenerMock).onChange();
+        verify(mListenerMock, times(2)).onChange();
     }
 
     @Test
@@ -265,8 +267,7 @@
         // Trigger multiple observers manually.
         mVibrationSettings.mSettingObserver.onChange(false);
         mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
-        mVibrationSettings.mSettingChangeReceiver.onReceive(mContextSpy,
-                new Intent(Intent.ACTION_USER_SWITCHED));
+        mVibrationSettings.mUserSwitchObserver.onUserSwitchComplete(NEW_USER_ID);
         mVibrationSettings.mSettingChangeReceiver.onReceive(mContextSpy,
                 new Intent(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION));
 
@@ -834,13 +835,17 @@
         assertEquals(VIBRATION_INTENSITY_HIGH,
                 mVibrationSettings.getCurrentIntensity(USAGE_RINGTONE));
 
-        // Switching user is not working with FakeSettingsProvider.
-        // Testing the broadcast flow manually.
-        Settings.System.putIntForUser(mContextSpy.getContentResolver(),
-                Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW,
+        // Test early update of settings based on new user id.
+        putUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW,
+                NEW_USER_ID);
+        mVibrationSettings.mUserSwitchObserver.onUserSwitching(NEW_USER_ID);
+        assertEquals(VIBRATION_INTENSITY_LOW,
+                mVibrationSettings.getCurrentIntensity(USAGE_RINGTONE));
+
+        // Test later update of settings for UserHandle.USER_CURRENT.
+        putUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW,
                 UserHandle.USER_CURRENT);
-        mVibrationSettings.mSettingChangeReceiver.onReceive(mContextSpy,
-                new Intent(Intent.ACTION_USER_SWITCHED));
+        mVibrationSettings.mUserSwitchObserver.onUserSwitchComplete(NEW_USER_ID);
         assertEquals(VIBRATION_INTENSITY_LOW,
                 mVibrationSettings.getCurrentIntensity(USAGE_RINGTONE));
     }
@@ -956,12 +961,16 @@
     }
 
     private void setUserSetting(String settingName, int value) {
-        Settings.System.putIntForUser(
-                mContextSpy.getContentResolver(), settingName, value, UserHandle.USER_CURRENT);
+        putUserSetting(settingName, value, UserHandle.USER_CURRENT);
         // FakeSettingsProvider doesn't support testing triggering ContentObserver yet.
         mVibrationSettings.mSettingObserver.onChange(false);
     }
 
+    private void putUserSetting(String settingName, int value, int userHandle) {
+        Settings.System.putIntForUser(
+                mContextSpy.getContentResolver(), settingName, value, userHandle);
+    }
+
     private void setRingerMode(int ringerMode) {
         when(mAudioManagerMock.getRingerModeInternal()).thenReturn(ringerMode);
         // Mock AudioManager broadcast of internal ringer mode change.
diff --git a/services/tests/wmtests/src/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncherTest.java b/services/tests/wmtests/src/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncherTest.java
index 08e6396..402b704 100644
--- a/services/tests/wmtests/src/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncherTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncherTest.java
@@ -16,33 +16,23 @@
 
 package com.android.server.wm;
 
-import static android.view.WindowManager.TRANSIT_CHANGE;
-
+import static com.android.internal.R.bool.config_unfoldTransitionEnabled;
+import static com.android.server.wm.DeviceStateController.DeviceState.REAR;
 import static com.android.server.wm.DeviceStateController.DeviceState.FOLDED;
 import static com.android.server.wm.DeviceStateController.DeviceState.HALF_FOLDED;
 import static com.android.server.wm.DeviceStateController.DeviceState.OPEN;
 
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.when;
 
 import android.animation.ValueAnimator;
-import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
-import android.window.TransitionRequestInfo.DisplayChange;
-
-import static com.android.internal.R.bool.config_unfoldTransitionEnabled;
-import static com.android.server.wm.DeviceStateController.DeviceState.REAR;
 
 import androidx.test.filters.SmallTest;
 
@@ -50,7 +40,6 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -62,20 +51,19 @@
  */
 @SmallTest
 @Presubmit
-public class PhysicalDisplaySwitchTransitionLauncherTest {
+@RunWith(WindowTestRunner.class)
+public class PhysicalDisplaySwitchTransitionLauncherTest extends WindowTestsBase {
 
     @Mock
-    DisplayContent mDisplayContent;
-    @Mock
     Context mContext;
     @Mock
     Resources mResources;
     @Mock
-    ActivityTaskManagerService mActivityTaskManagerService;
-    @Mock
     BLASTSyncEngine mSyncEngine;
-    @Mock
+
+    WindowTestsBase.TestTransitionPlayer mPlayer;
     TransitionController mTransitionController;
+    DisplayContent mDisplayContent;
 
     private PhysicalDisplaySwitchTransitionLauncher mTarget;
     private float mOriginalAnimationScale;
@@ -83,9 +71,14 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+        mTransitionController = new WindowTestsBase.TestTransitionController(mAtm);
+        mTransitionController.setSyncEngine(mSyncEngine);
+        mPlayer = new WindowTestsBase.TestTransitionPlayer(
+                mTransitionController, mAtm.mWindowOrganizerController);
         when(mContext.getResources()).thenReturn(mResources);
-        mTarget = new PhysicalDisplaySwitchTransitionLauncher(mDisplayContent,
-                mActivityTaskManagerService, mContext, mTransitionController);
+        mDisplayContent = new TestDisplayContent.Builder(mAtm, 100, 150).build();
+        mTarget = new PhysicalDisplaySwitchTransitionLauncher(mDisplayContent, mAtm, mContext,
+                mTransitionController);
         mOriginalAnimationScale = ValueAnimator.getDurationScale();
     }
 
@@ -100,24 +93,23 @@
         mTarget.foldStateChanged(FOLDED);
 
         mTarget.foldStateChanged(OPEN);
+        final Rect origBounds = new Rect();
+        mDisplayContent.getBounds(origBounds);
+        origBounds.offsetTo(0, 0);
         mTarget.requestDisplaySwitchTransitionIfNeeded(
-                /* displayId= */ 123,
-                /* oldDisplayWidth= */ 100,
-                /* oldDisplayHeight= */ 150,
+                mDisplayContent.getDisplayId(),
+                origBounds.width(),
+                origBounds.height(),
                 /* newDisplayWidth= */ 200,
                 /* newDisplayHeight= */ 250
         );
 
-        ArgumentCaptor<DisplayChange> displayChangeArgumentCaptor =
-                ArgumentCaptor.forClass(DisplayChange.class);
-        verify(mTransitionController).requestTransitionIfNeeded(eq(TRANSIT_CHANGE), /* flags= */
-                eq(0), eq(mDisplayContent), eq(mDisplayContent), /* remoteTransition= */ isNull(),
-                displayChangeArgumentCaptor.capture());
-        assertThat(displayChangeArgumentCaptor.getValue().getDisplayId()).isEqualTo(123);
-        assertThat(displayChangeArgumentCaptor.getValue().getStartAbsBounds()).isEqualTo(
-                new Rect(0, 0, 100, 150));
-        assertThat(displayChangeArgumentCaptor.getValue().getEndAbsBounds()).isEqualTo(
-                new Rect(0, 0, 200, 250));
+        assertNotNull(mPlayer.mLastRequest);
+        assertEquals(mDisplayContent.getDisplayId(),
+                mPlayer.mLastRequest.getDisplayChange().getDisplayId());
+        assertEquals(origBounds, mPlayer.mLastRequest.getDisplayChange().getStartAbsBounds());
+        assertEquals(new Rect(0, 0, 200, 250),
+                mPlayer.mLastRequest.getDisplayChange().getEndAbsBounds());
     }
 
     @Test
@@ -148,7 +140,7 @@
         mTarget.foldStateChanged(FOLDED);
         mTarget.foldStateChanged(OPEN);
         requestDisplaySwitch();
-        clearInvocations(mTransitionController);
+        mPlayer.mLastRequest = null;
 
         requestDisplaySwitch();
 
@@ -220,7 +212,6 @@
 
     @Test
     public void testDisplaySwitchAfterUnfolding_otherCollectingTransition_collectsDisplaySwitch() {
-        givenCollectingTransition(createTransition(TRANSIT_CHANGE));
         givenAllAnimationsEnabled();
         mTarget.foldStateChanged(FOLDED);
 
@@ -228,7 +219,8 @@
         requestDisplaySwitch();
 
         // Collects to the current transition
-        verify(mTransitionController).collect(mDisplayContent);
+        assertTrue(mTransitionController.getCollectingTransition().mParticipants.contains(
+                mDisplayContent));
     }
 
 
@@ -245,20 +237,18 @@
     }
 
     private void assertTransitionRequested() {
-        verify(mTransitionController).requestTransitionIfNeeded(anyInt(), anyInt(), any(), any(),
-                any(), any());
+        assertNotNull(mPlayer.mLastRequest);
     }
 
     private void assertTransitionNotRequested() {
-        verify(mTransitionController, never()).requestTransitionIfNeeded(anyInt(), anyInt(), any(),
-                any(), any(), any());
+        assertNull(mPlayer.mLastRequest);
     }
 
     private void requestDisplaySwitch() {
         mTarget.requestDisplaySwitchTransitionIfNeeded(
-                /* displayId= */ 123,
-                /* oldDisplayWidth= */ 100,
-                /* oldDisplayHeight= */ 150,
+                mDisplayContent.getDisplayId(),
+                mDisplayContent.getBounds().width(),
+                mDisplayContent.getBounds().height(),
                 /* newDisplayWidth= */ 200,
                 /* newDisplayHeight= */ 250
         );
@@ -280,16 +270,11 @@
     }
 
     private void givenShellTransitionsEnabled(boolean enabled) {
-        when(mTransitionController.isShellTransitionsEnabled()).thenReturn(enabled);
-    }
-
-    private void givenCollectingTransition(@Nullable Transition transition) {
-        when(mTransitionController.isCollecting()).thenReturn(transition != null);
-        when(mTransitionController.getCollectingTransition()).thenReturn(transition);
-    }
-
-    private Transition createTransition(int type) {
-        return new Transition(type, /* flags= */ 0, mTransitionController, mSyncEngine);
+        if (enabled) {
+            mTransitionController.registerTransitionPlayer(mPlayer, null /* proc */);
+        } else {
+            mTransitionController.detachPlayer();
+        }
     }
 
     private void givenDisplayContentHasContent(boolean hasContent) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index d3f69b5..b96f39d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -22,6 +22,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
@@ -215,90 +216,46 @@
 
     @Test
     public void testHorizontalReachabilityEnabledForTranslucentActivities() {
-        setUpDisplaySizeWithApp(2500, 1000);
-        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
-        final LetterboxConfiguration config = mWm.mLetterboxConfiguration;
-        config.setTranslucentLetterboxingOverrideEnabled(true);
-        config.setLetterboxHorizontalPositionMultiplier(0.5f);
-        config.setIsHorizontalReachabilityEnabled(true);
+        testReachabilityEnabledForTranslucentActivity(/* dw */ 2500,  /* dh */1000,
+                SCREEN_ORIENTATION_PORTRAIT, /* minAspectRatio */ 0f,
+                /* horizontalReachability */ true);
+    }
 
-        // Opaque activity
-        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
-        addWindowToActivity(mActivity);
-        mActivity.mRootWindowContainer.performSurfacePlacement();
-
-        // Translucent Activity
-        final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
-                .setActivityTheme(android.R.style.Theme_Translucent)
-                .setLaunchedFromUid(mActivity.getUid())
-                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
-                .build();
-        mTask.addChild(translucentActivity);
-
-        spyOn(translucentActivity.mLetterboxUiController);
-        doReturn(true).when(translucentActivity.mLetterboxUiController)
-                .shouldShowLetterboxUi(any());
-
-        addWindowToActivity(translucentActivity);
-        translucentActivity.mRootWindowContainer.performSurfacePlacement();
-
-        final Function<ActivityRecord, Rect> innerBoundsOf =
-                (ActivityRecord a) -> {
-                    final Rect bounds = new Rect();
-                    a.mLetterboxUiController.getLetterboxInnerBounds(bounds);
-                    return bounds;
-                };
-        final Runnable checkLetterboxPositions = () -> assertEquals(innerBoundsOf.apply(mActivity),
-                innerBoundsOf.apply(translucentActivity));
-        final Runnable checkIsLeft = () -> assertThat(
-                innerBoundsOf.apply(translucentActivity).left).isEqualTo(0);
-        final Runnable checkIsRight = () -> assertThat(
-                innerBoundsOf.apply(translucentActivity).right).isEqualTo(2500);
-        final Runnable checkIsCentered = () -> assertThat(
-                innerBoundsOf.apply(translucentActivity).left > 0
-                        && innerBoundsOf.apply(translucentActivity).right < 2500).isTrue();
-
-        final Consumer<Integer> doubleClick =
-                (Integer x) -> {
-                    mActivity.mLetterboxUiController.handleHorizontalDoubleTap(x);
-                    mActivity.mRootWindowContainer.performSurfacePlacement();
-                };
-
-        // Initial state
-        checkIsCentered.run();
-
-        // Double-click left
-        doubleClick.accept(/* x */ 10);
-        checkLetterboxPositions.run();
-        checkIsLeft.run();
-
-        // Double-click right
-        doubleClick.accept(/* x */ 1990);
-        checkLetterboxPositions.run();
-        checkIsCentered.run();
-
-        // Double-click right
-        doubleClick.accept(/* x */ 1990);
-        checkLetterboxPositions.run();
-        checkIsRight.run();
-
-        // Double-click left
-        doubleClick.accept(/* x */ 10);
-        checkLetterboxPositions.run();
-        checkIsCentered.run();
+    @Test
+    public void testHorizontalReachabilityEnabled_TranslucentPortraitActivities_portraitDisplay() {
+        testReachabilityEnabledForTranslucentActivity(/* dw */ 1400,  /* dh */1600,
+                SCREEN_ORIENTATION_PORTRAIT, OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE,
+                /* horizontalReachability */ true);
     }
 
     @Test
     public void testVerticalReachabilityEnabledForTranslucentActivities() {
-        setUpDisplaySizeWithApp(1000, 2500);
+        testReachabilityEnabledForTranslucentActivity(/* dw */ 1000,  /* dh */2500,
+                SCREEN_ORIENTATION_LANDSCAPE, /* minAspectRatio */ 0f,
+                /* horizontalReachability */ false);
+    }
+
+    @Test
+    public void testVerticalReachabilityEnabled_TranslucentLandscapeActivities_landscapeDisplay() {
+        testReachabilityEnabledForTranslucentActivity(/* dw */ 1600,  /* dh */1400,
+                SCREEN_ORIENTATION_LANDSCAPE, OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE,
+                /* horizontalReachability */ false);
+    }
+
+    private void testReachabilityEnabledForTranslucentActivity(int displayWidth, int displayHeight,
+            @ScreenOrientation int screenOrientation, float minAspectRatio,
+            boolean horizontalReachability) {
+        setUpDisplaySizeWithApp(displayWidth, displayHeight);
         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
         final LetterboxConfiguration config = mWm.mLetterboxConfiguration;
         config.setTranslucentLetterboxingOverrideEnabled(true);
         config.setLetterboxVerticalPositionMultiplier(0.5f);
         config.setIsVerticalReachabilityEnabled(true);
+        config.setLetterboxHorizontalPositionMultiplier(0.5f);
+        config.setIsHorizontalReachabilityEnabled(true);
 
         // Opaque activity
-        prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
+        prepareMinAspectRatio(mActivity, minAspectRatio, screenOrientation);
         addWindowToActivity(mActivity);
         mActivity.mRootWindowContainer.performSurfacePlacement();
 
@@ -306,7 +263,7 @@
         final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
                 .setActivityTheme(android.R.style.Theme_Translucent)
                 .setLaunchedFromUid(mActivity.getUid())
-                .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE)
+                .setScreenOrientation(screenOrientation)
                 .build();
         mTask.addChild(translucentActivity);
 
@@ -328,39 +285,78 @@
         final Runnable checkIsTop = () -> assertThat(
                 innerBoundsOf.apply(translucentActivity).top).isEqualTo(0);
         final Runnable checkIsBottom = () -> assertThat(
-                innerBoundsOf.apply(translucentActivity).bottom).isEqualTo(2500);
-        final Runnable checkIsCentered = () -> assertThat(
+                innerBoundsOf.apply(translucentActivity).bottom).isEqualTo(displayHeight);
+        final Runnable checkIsLeft = () -> assertThat(
+                innerBoundsOf.apply(translucentActivity).left).isEqualTo(0);
+        final Runnable checkIsRight = () -> assertThat(
+                innerBoundsOf.apply(translucentActivity).right).isEqualTo(displayWidth);
+        final Runnable checkIsHorizontallyCentered = () -> assertThat(
+                innerBoundsOf.apply(translucentActivity).left > 0
+                        && innerBoundsOf.apply(translucentActivity).right < displayWidth).isTrue();
+        final Runnable checkIsVerticallyCentered = () -> assertThat(
                 innerBoundsOf.apply(translucentActivity).top > 0
-                        && innerBoundsOf.apply(translucentActivity).bottom < 2500).isTrue();
+                        && innerBoundsOf.apply(translucentActivity).bottom < displayHeight)
+                .isTrue();
 
-        final Consumer<Integer> doubleClick =
-                (Integer y) -> {
-                    mActivity.mLetterboxUiController.handleVerticalDoubleTap(y);
-                    mActivity.mRootWindowContainer.performSurfacePlacement();
-                };
+        if (horizontalReachability) {
+            final Consumer<Integer> doubleClick =
+                    (Integer x) -> {
+                        mActivity.mLetterboxUiController.handleHorizontalDoubleTap(x);
+                        mActivity.mRootWindowContainer.performSurfacePlacement();
+                    };
 
-        // Initial state
-        checkIsCentered.run();
+            // Initial state
+            checkIsHorizontallyCentered.run();
 
-        // Double-click top
-        doubleClick.accept(/* y */ 10);
-        checkLetterboxPositions.run();
-        checkIsTop.run();
+            // Double-click left
+            doubleClick.accept(/* x */ 10);
+            checkLetterboxPositions.run();
+            checkIsLeft.run();
 
-        // Double-click bottom
-        doubleClick.accept(/* y */ 1990);
-        checkLetterboxPositions.run();
-        checkIsCentered.run();
+            // Double-click right
+            doubleClick.accept(/* x */ displayWidth - 100);
+            checkLetterboxPositions.run();
+            checkIsHorizontallyCentered.run();
 
-        // Double-click bottom
-        doubleClick.accept(/* y */ 1990);
-        checkLetterboxPositions.run();
-        checkIsBottom.run();
+            // Double-click right
+            doubleClick.accept(/* x */ displayWidth - 100);
+            checkLetterboxPositions.run();
+            checkIsRight.run();
 
-        // Double-click top
-        doubleClick.accept(/* y */ 10);
-        checkLetterboxPositions.run();
-        checkIsCentered.run();
+            // Double-click left
+            doubleClick.accept(/* x */ 10);
+            checkLetterboxPositions.run();
+            checkIsHorizontallyCentered.run();
+        } else {
+            final Consumer<Integer> doubleClick =
+                    (Integer y) -> {
+                        mActivity.mLetterboxUiController.handleVerticalDoubleTap(y);
+                        mActivity.mRootWindowContainer.performSurfacePlacement();
+                    };
+
+            // Initial state
+            checkIsVerticallyCentered.run();
+
+            // Double-click top
+            doubleClick.accept(/* y */ 10);
+            checkLetterboxPositions.run();
+            checkIsTop.run();
+
+            // Double-click bottom
+            doubleClick.accept(/* y */ displayHeight - 100);
+            checkLetterboxPositions.run();
+            checkIsVerticallyCentered.run();
+
+            // Double-click bottom
+            doubleClick.accept(/* y */ displayHeight - 100);
+            checkLetterboxPositions.run();
+            checkIsBottom.run();
+
+            // Double-click top
+            doubleClick.accept(/* y */ 10);
+            checkLetterboxPositions.run();
+            checkIsVerticallyCentered.run();
+        }
     }
 
     @Test
@@ -3609,6 +3605,32 @@
     }
 
     @Test
+    public void testIsHorizontalReachabilityEnabled_portraitDisplayAndApp_true() {
+        // Portrait display
+        setUpDisplaySizeWithApp(1400, 1600);
+        mActivity.mWmService.mLetterboxConfiguration.setIsHorizontalReachabilityEnabled(true);
+
+        // 16:9f unresizable portrait app
+        prepareMinAspectRatio(mActivity, OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE,
+                SCREEN_ORIENTATION_PORTRAIT);
+
+        assertTrue(mActivity.mLetterboxUiController.isHorizontalReachabilityEnabled());
+    }
+
+    @Test
+    public void testIsVerticalReachabilityEnabled_landscapeDisplayAndApp_true() {
+        // Landscape display
+        setUpDisplaySizeWithApp(1600, 1500);
+        mActivity.mWmService.mLetterboxConfiguration.setIsVerticalReachabilityEnabled(true);
+
+        // 16:9f unresizable landscape app
+        prepareMinAspectRatio(mActivity, OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE,
+                SCREEN_ORIENTATION_LANDSCAPE);
+
+        assertTrue(mActivity.mLetterboxUiController.isVerticalReachabilityEnabled());
+    }
+
+    @Test
     public void testIsHorizontalReachabilityEnabled_doesNotMatchParentHeight_false() {
         setUpDisplaySizeWithApp(2800, 1000);
         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
@@ -4884,6 +4906,12 @@
                 .build();
     }
 
+    static void prepareMinAspectRatio(ActivityRecord activity, float minAspect,
+            int screenOrientation) {
+        prepareLimitedBounds(activity, -1 /* maxAspect */, minAspect, screenOrientation,
+                true /* isUnresizable */);
+    }
+
     static void prepareUnresizable(ActivityRecord activity, int screenOrientation) {
         prepareUnresizable(activity, -1 /* maxAspect */, screenOrientation);
     }
@@ -4898,12 +4926,18 @@
         prepareLimitedBounds(activity, -1 /* maxAspect */, screenOrientation, isUnresizable);
     }
 
-    /**
-     * Setups {@link #mActivity} with restriction on its bounds, such as maxAspect, fixed
-     * orientation, and/or whether it is resizable.
-     */
     static void prepareLimitedBounds(ActivityRecord activity, float maxAspect,
             int screenOrientation, boolean isUnresizable) {
+        prepareLimitedBounds(activity, maxAspect, -1 /* minAspect */, screenOrientation,
+                isUnresizable);
+    }
+
+    /**
+     * Setups {@link #mActivity} with restriction on its bounds, such as maxAspect, minAspect,
+     * fixed orientation, and/or whether it is resizable.
+     */
+    static void prepareLimitedBounds(ActivityRecord activity, float maxAspect, float minAspect,
+            int screenOrientation, boolean isUnresizable) {
         activity.info.resizeMode = isUnresizable
                 ? RESIZE_MODE_UNRESIZEABLE
                 : RESIZE_MODE_RESIZEABLE;
@@ -4917,6 +4951,9 @@
         if (maxAspect >= 0) {
             activity.info.setMaxAspectRatio(maxAspect);
         }
+        if (minAspect >= 0) {
+            activity.info.setMinAspectRatio(minAspect);
+        }
         if (screenOrientation != SCREEN_ORIENTATION_UNSPECIFIED) {
             activity.info.screenOrientation = screenOrientation;
             activity.setRequestedOrientation(screenOrientation);
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