Merge "Track creation of a11y overlays using an AppOp" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index e3cbd92..2457e70 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -57,6 +57,7 @@
     ":android.service.autofill.flags-aconfig-java{.generated_srcjars}",
     ":com.android.net.flags-aconfig-java{.generated_srcjars}",
     ":device_policy_aconfig_flags_lib{.generated_srcjars}",
+    ":surfaceflinger_flags_java_lib{.generated_srcjars}",
 ]
 
 filegroup {
@@ -468,7 +469,7 @@
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
 
-// Activity Manager
+// android.app
 aconfig_declarations {
     name: "android.app.flags-aconfig",
     package: "android.app",
@@ -627,3 +628,10 @@
     aconfig_declarations: "android.app.smartspace.flags-aconfig",
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
+
+// SurfaceFlinger
+java_aconfig_library {
+    name: "surfaceflinger_flags_java_lib",
+    aconfig_declarations: "surfaceflinger_flags",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
diff --git a/Android.bp b/Android.bp
index 49256dd..8c4d769 100644
--- a/Android.bp
+++ b/Android.bp
@@ -745,90 +745,36 @@
 // non_updatable_modules list in frameworks/base/api/api.go
 java_defaults {
     name: "framework-non-updatable-unbundled-defaults",
-    defaults: ["framework-non-updatable-lint-defaults"],
-
-    sdk_version: "core_platform",
-
-    // Api scope settings
+    defaults: [
+        "framework-non-updatable-lint-defaults",
+        "non-updatable-framework-module-defaults",
+    ],
     public: {
-        enabled: true,
-        sdk_version: "module_current",
         libs: ["android_module_lib_stubs_current"],
     },
     system: {
-        enabled: true,
-        sdk_version: "module_current",
         libs: ["android_module_lib_stubs_current"],
     },
     module_lib: {
-        enabled: true,
-        sdk_version: "module_current",
         libs: ["android_module_lib_stubs_current"],
     },
     test: {
-        enabled: true,
-        sdk_version: "test_frameworks_core_current",
         libs: ["android_test_frameworks_core_stubs_current"],
     },
-
-    stub_only_libs: [
-        "framework-protos",
-    ],
-    impl_only_libs: [
-        "framework-minus-apex-headers", // full access to framework-minus-apex including hidden API
-        "framework-annotations-lib",
-    ],
-    visibility: ["//visibility:public"],
-    stubs_library_visibility: ["//visibility:public"],
-    stubs_source_visibility: ["//visibility:private"],
-    impl_library_visibility: [
-        ":__pkg__",
-        "//frameworks/base",
-        "//frameworks/base/api", // For framework-all
-    ],
-    defaults_visibility: [
-        "//frameworks/base/location",
-    ],
-    plugins: [
-        "error_prone_android_framework",
-    ],
+    sdk_version: "core_platform",
+    stub_only_libs: ["framework-protos"],
+    impl_only_libs: ["framework-minus-apex-headers"], // the framework, including hidden API
+    impl_library_visibility: ["//frameworks/base"],
+    defaults_visibility: ["//frameworks/base/location"],
+    plugins: ["error_prone_android_framework"],
     errorprone: {
         javacflags: [
             "-Xep:AndroidFrameworkCompatChange:ERROR",
             "-Xep:AndroidFrameworkUid:ERROR",
         ],
     },
-
     // Include manual annotations in API txt files
     merge_annotations_dirs: ["metalava-manual"],
-
-    // Use the source of annotations that affect metalava doc generation, since
-    // the relevant generation instructions are themselves in javadoc, which is
-    // not present in class files.
-    api_srcs: [":framework-metalava-annotations"],
-
-    // Framework modules are not generally shared libraries, i.e. they are not
-    // intended, and must not be allowed, to be used in a <uses-library> manifest
-    // entry.
-    shared_library: false,
-
-    // Prevent dependencies that do not specify an sdk_version from accessing the
-    // implementation library by default and force them to use stubs instead.
-    default_to_stubs: true,
-
-    // Subdirectory for the artifacts that are copied to the dist directory
-    dist_group: "android",
-
-    droiddoc_options: [
-        "--error UnhiddenSystemApi " +
-            "--hide CallbackInterface " +
-            "--hide HiddenTypedefConstant " +
-            "--hide RequiresPermission " +
-            "--enhance-documentation " +
-            "--hide-package com.android.server ",
-    ],
-
-    annotations_enabled: true,
 }
 
 build = [
diff --git a/BAL_OWNERS b/BAL_OWNERS
new file mode 100644
index 0000000..d56a1d4
--- /dev/null
+++ b/BAL_OWNERS
@@ -0,0 +1,5 @@
+brufino@google.com
+achim@google.com
+topjohnwu@google.com
+lus@google.com
+
diff --git a/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
index e08200b..5fc7745 100644
--- a/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
+++ b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
@@ -23,6 +23,7 @@
 import android.app.AppOpsManager.PackageOps;
 import android.app.IActivityManager;
 import android.app.usage.UsageStatsManager;
+import android.content.AttributionSource;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -746,8 +747,10 @@
         public void opChanged(int op, int uid, String packageName) throws RemoteException {
             boolean restricted = false;
             try {
-                restricted = mAppOpsService.checkOperation(TARGET_OP,
-                        uid, packageName) != AppOpsManager.MODE_ALLOWED;
+                final AttributionSource attributionSource =
+                        new AttributionSource.Builder(uid).setPackageName(packageName).build();
+                restricted = mAppOpsService.checkOperationWithState(TARGET_OP,
+                        attributionSource.asState()) != AppOpsManager.MODE_ALLOWED;
             } catch (RemoteException e) {
                 // Shouldn't happen
             }
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 1287cb4..23b36e2 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -1820,7 +1820,11 @@
                     jobStatus.getEstimatedNetworkUploadBytes(),
                     jobStatus.getWorkCount(),
                     ActivityManager.processStateAmToProto(mUidProcStates.get(jobStatus.getUid())),
-                    jobStatus.getNamespaceHash());
+                    jobStatus.getNamespaceHash(),
+                    /* system_measured_source_download_bytes */0,
+                    /* system_measured_source_upload_bytes */ 0,
+                    /* system_measured_calling_download_bytes */0,
+                    /* system_measured_calling_upload_bytes */ 0);
 
             // If the job is immediately ready to run, then we can just immediately
             // put it in the pending list and try to schedule it.  This is especially
@@ -2257,7 +2261,11 @@
                     cancelled.getEstimatedNetworkUploadBytes(),
                     cancelled.getWorkCount(),
                     ActivityManager.processStateAmToProto(mUidProcStates.get(cancelled.getUid())),
-                    cancelled.getNamespaceHash());
+                    cancelled.getNamespaceHash(),
+                    /* system_measured_source_download_bytes */ 0,
+                    /* system_measured_source_upload_bytes */ 0,
+                    /* system_measured_calling_download_bytes */0,
+                    /* system_measured_calling_upload_bytes */ 0);
         }
         // If this is a replacement, bring in the new version of the job
         if (incomingJob != null) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index 79653f0..2d49cfb 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -45,6 +45,7 @@
 import android.content.PermissionChecker;
 import android.content.ServiceConnection;
 import android.net.Network;
+import android.net.TrafficStats;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
@@ -241,6 +242,14 @@
     private int mDeathMarkInternalStopReason;
     private String mDeathMarkDebugReason;
 
+    private long mInitialDownloadedBytesFromSource;
+
+    private long mInitialUploadedBytesFromSource;
+
+    private long mInitialDownloadedBytesFromCalling;
+
+    private long mInitialUploadedBytesFromCalling;
+
     // Debugging: reason this job was last stopped.
     public String mStoppedReason;
 
@@ -472,6 +481,14 @@
             }
             mJobPackageTracker.noteActive(job);
             final int sourceUid = job.getSourceUid();
+
+            // Measure UID baseline traffic for deltas
+            mInitialDownloadedBytesFromSource = TrafficStats.getUidRxBytes(sourceUid);
+            mInitialUploadedBytesFromSource = TrafficStats.getUidTxBytes(sourceUid);
+
+            mInitialDownloadedBytesFromCalling = TrafficStats.getUidRxBytes(job.getUid());
+            mInitialUploadedBytesFromCalling = TrafficStats.getUidTxBytes(job.getUid());
+
             FrameworkStatsLog.write(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED,
                     job.isProxyJob() ? new int[]{sourceUid, job.getUid()} : new int[]{sourceUid},
                     // Given that the source tag is set by the calling app, it should be connected
@@ -517,7 +534,11 @@
                     job.getEstimatedNetworkUploadBytes(),
                     job.getWorkCount(),
                     ActivityManager.processStateAmToProto(mService.getUidProcState(job.getUid())),
-                    job.getNamespaceHash());
+                    job.getNamespaceHash(),
+                    /* system_measured_source_download_bytes */ 0,
+                    /* system_measured_source_upload_bytes */ 0,
+                    /* system_measured_calling_download_bytes */ 0,
+                    /* system_measured_calling_upload_bytes */ 0);
             sEnqueuedJwiAtJobStart.logSampleWithUid(job.getUid(), job.getWorkCount());
             final String sourcePackage = job.getSourcePackageName();
             if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
@@ -1586,7 +1607,15 @@
                 completedJob.getWorkCount(),
                 ActivityManager
                         .processStateAmToProto(mService.getUidProcState(completedJob.getUid())),
-                completedJob.getNamespaceHash());
+                completedJob.getNamespaceHash(),
+                TrafficStats.getUidRxBytes(completedJob.getSourceUid())
+                        - mInitialDownloadedBytesFromSource,
+                TrafficStats.getUidTxBytes(completedJob.getSourceUid())
+                        - mInitialUploadedBytesFromSource,
+                TrafficStats.getUidRxBytes(completedJob.getUid())
+                        - mInitialDownloadedBytesFromCalling,
+                TrafficStats.getUidTxBytes(completedJob.getUid())
+                        - mInitialUploadedBytesFromCalling);
         if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
             Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_SYSTEM_SERVER, "JobScheduler",
                     getId());
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
index b8397d2..95f901c 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
@@ -38,6 +38,7 @@
 import android.app.tare.IEconomyManager;
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageStatsManagerInternal;
+import android.content.AttributionSource;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -230,8 +231,11 @@
         public void opChanged(int op, int uid, String packageName) {
             boolean restricted = false;
             try {
-                restricted = mAppOpsService.checkOperation(
-                        AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, uid, packageName)
+                final AttributionSource attributionSource = new AttributionSource.Builder(uid)
+                        .setPackageName(packageName)
+                        .build();
+                restricted = mAppOpsService.checkOperationWithState(
+                        AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, attributionSource.asState())
                         != AppOpsManager.MODE_ALLOWED;
             } catch (RemoteException e) {
                 // Shouldn't happen
diff --git a/core/api/current.txt b/core/api/current.txt
index fdb4133..cb293c8 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -11989,22 +11989,22 @@
     method public final int compare(android.content.pm.ApplicationInfo, android.content.pm.ApplicationInfo);
   }
 
-  @FlaggedApi("android.content.pm.archiving") public final class ArchivedActivity {
-    ctor public ArchivedActivity(@NonNull CharSequence, @NonNull android.content.ComponentName);
+  @FlaggedApi("android.content.pm.archiving") public final class ArchivedActivityInfo {
+    ctor public ArchivedActivityInfo(@NonNull CharSequence, @NonNull android.content.ComponentName);
     method @NonNull public android.content.ComponentName getComponentName();
     method @Nullable public android.graphics.drawable.Drawable getIcon();
     method @NonNull public CharSequence getLabel();
     method @Nullable public android.graphics.drawable.Drawable getMonochromeIcon();
-    method @NonNull public android.content.pm.ArchivedActivity setComponentName(@NonNull android.content.ComponentName);
-    method @NonNull public android.content.pm.ArchivedActivity setIcon(@NonNull android.graphics.drawable.Drawable);
-    method @NonNull public android.content.pm.ArchivedActivity setLabel(@NonNull CharSequence);
-    method @NonNull public android.content.pm.ArchivedActivity setMonochromeIcon(@NonNull android.graphics.drawable.Drawable);
+    method @NonNull public android.content.pm.ArchivedActivityInfo setComponentName(@NonNull android.content.ComponentName);
+    method @NonNull public android.content.pm.ArchivedActivityInfo setIcon(@NonNull android.graphics.drawable.Drawable);
+    method @NonNull public android.content.pm.ArchivedActivityInfo setLabel(@NonNull CharSequence);
+    method @NonNull public android.content.pm.ArchivedActivityInfo setMonochromeIcon(@NonNull android.graphics.drawable.Drawable);
   }
 
-  @FlaggedApi("android.content.pm.archiving") public final class ArchivedPackage {
-    ctor public ArchivedPackage(@NonNull String, @NonNull android.content.pm.SigningInfo, @NonNull java.util.List<android.content.pm.ArchivedActivity>);
+  @FlaggedApi("android.content.pm.archiving") public final class ArchivedPackageInfo {
+    ctor public ArchivedPackageInfo(@NonNull String, @NonNull android.content.pm.SigningInfo, @NonNull java.util.List<android.content.pm.ArchivedActivityInfo>);
     method @Nullable public String getDefaultToDeviceProtectedStorage();
-    method @NonNull public java.util.List<android.content.pm.ArchivedActivity> getLauncherActivities();
+    method @NonNull public java.util.List<android.content.pm.ArchivedActivityInfo> getLauncherActivities();
     method @NonNull public String getPackageName();
     method @Nullable public String getRequestLegacyExternalStorage();
     method @NonNull public android.content.pm.SigningInfo getSigningInfo();
@@ -12012,15 +12012,15 @@
     method @Nullable public String getUserDataFragile();
     method public int getVersionCode();
     method public int getVersionCodeMajor();
-    method @NonNull public android.content.pm.ArchivedPackage setDefaultToDeviceProtectedStorage(@NonNull String);
-    method @NonNull public android.content.pm.ArchivedPackage setLauncherActivities(@NonNull java.util.List<android.content.pm.ArchivedActivity>);
-    method @NonNull public android.content.pm.ArchivedPackage setPackageName(@NonNull String);
-    method @NonNull public android.content.pm.ArchivedPackage setRequestLegacyExternalStorage(@NonNull String);
-    method @NonNull public android.content.pm.ArchivedPackage setSigningInfo(@NonNull android.content.pm.SigningInfo);
-    method @NonNull public android.content.pm.ArchivedPackage setTargetSdkVersion(int);
-    method @NonNull public android.content.pm.ArchivedPackage setUserDataFragile(@NonNull String);
-    method @NonNull public android.content.pm.ArchivedPackage setVersionCode(int);
-    method @NonNull public android.content.pm.ArchivedPackage setVersionCodeMajor(int);
+    method @NonNull public android.content.pm.ArchivedPackageInfo setDefaultToDeviceProtectedStorage(@NonNull String);
+    method @NonNull public android.content.pm.ArchivedPackageInfo setLauncherActivities(@NonNull java.util.List<android.content.pm.ArchivedActivityInfo>);
+    method @NonNull public android.content.pm.ArchivedPackageInfo setPackageName(@NonNull String);
+    method @NonNull public android.content.pm.ArchivedPackageInfo setRequestLegacyExternalStorage(@NonNull String);
+    method @NonNull public android.content.pm.ArchivedPackageInfo setSigningInfo(@NonNull android.content.pm.SigningInfo);
+    method @NonNull public android.content.pm.ArchivedPackageInfo setTargetSdkVersion(int);
+    method @NonNull public android.content.pm.ArchivedPackageInfo setUserDataFragile(@NonNull String);
+    method @NonNull public android.content.pm.ArchivedPackageInfo setVersionCode(int);
+    method @NonNull public android.content.pm.ArchivedPackageInfo setVersionCodeMajor(int);
   }
 
   public final class Attribution implements android.os.Parcelable {
@@ -12355,7 +12355,7 @@
     method @Nullable public android.content.pm.PackageInstaller.SessionInfo getSessionInfo(int);
     method @NonNull public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getStagedSessions();
     method @RequiresPermission(allOf={android.Manifest.permission.INSTALL_PACKAGES, "com.android.permission.INSTALL_EXISTING_PACKAGES"}) public void installExistingPackage(@NonNull String, int, @Nullable android.content.IntentSender);
-    method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void installPackageArchived(@NonNull android.content.pm.ArchivedPackage, @NonNull android.content.pm.PackageInstaller.SessionParams, @NonNull android.content.IntentSender);
+    method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void installPackageArchived(@NonNull android.content.pm.ArchivedPackageInfo, @NonNull android.content.pm.PackageInstaller.SessionParams, @NonNull android.content.IntentSender);
     method @NonNull public android.content.pm.PackageInstaller.Session openSession(int) throws java.io.IOException;
     method public void registerSessionCallback(@NonNull android.content.pm.PackageInstaller.SessionCallback);
     method public void registerSessionCallback(@NonNull android.content.pm.PackageInstaller.SessionCallback, @NonNull android.os.Handler);
@@ -12637,7 +12637,7 @@
     method @NonNull public abstract CharSequence getApplicationLabel(@NonNull android.content.pm.ApplicationInfo);
     method @Nullable public abstract android.graphics.drawable.Drawable getApplicationLogo(@NonNull android.content.pm.ApplicationInfo);
     method @Nullable public abstract android.graphics.drawable.Drawable getApplicationLogo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
-    method @FlaggedApi("android.content.pm.archiving") @Nullable public android.content.pm.ArchivedPackage getArchivedPackage(@NonNull String);
+    method @FlaggedApi("android.content.pm.archiving") @Nullable public android.content.pm.ArchivedPackageInfo getArchivedPackage(@NonNull String);
     method @NonNull public CharSequence getBackgroundPermissionOptionLabel();
     method @Nullable public abstract android.content.pm.ChangedPackages getChangedPackages(@IntRange(from=0) int);
     method public abstract int getComponentEnabledSetting(@NonNull android.content.ComponentName);
@@ -17708,11 +17708,14 @@
 
 package android.graphics.text {
 
-  public final class LineBreakConfig {
+  public final class LineBreakConfig implements android.os.Parcelable {
+    method @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public int describeContents();
     method @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public int getHyphenation();
     method public int getLineBreakStyle();
     method public int getLineBreakWordStyle();
     method @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") @NonNull public android.graphics.text.LineBreakConfig merge(@NonNull android.graphics.text.LineBreakConfig);
+    method @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") @NonNull public static final android.os.Parcelable.Creator<android.graphics.text.LineBreakConfig> CREATOR;
     field @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public static final int HYPHENATION_DISABLED = 0; // 0x0
     field @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public static final int HYPHENATION_ENABLED = 1; // 0x1
     field @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public static final int HYPHENATION_UNSPECIFIED = -1; // 0xffffffff
@@ -42596,6 +42599,7 @@
     field public static final String EXTRA_CALL_DISCONNECT_CAUSE = "android.telecom.extra.CALL_DISCONNECT_CAUSE";
     field public static final String EXTRA_CALL_DISCONNECT_MESSAGE = "android.telecom.extra.CALL_DISCONNECT_MESSAGE";
     field public static final String EXTRA_CALL_DURATION = "android.telecom.extra.CALL_DURATION";
+    field @FlaggedApi("com.android.server.telecom.flags.add_call_uri_for_missed_calls") public static final String EXTRA_CALL_LOG_URI = "android.telecom.extra.CALL_LOG_URI";
     field public static final String EXTRA_CALL_NETWORK_TYPE = "android.telecom.extra.CALL_NETWORK_TYPE";
     field public static final String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT";
     field public static final String EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME = "android.telecom.extra.CHANGE_DEFAULT_DIALER_PACKAGE_NAME";
@@ -48011,17 +48015,15 @@
     method public void writeToParcel(@NonNull android.os.Parcel, int);
   }
 
-  @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public class LineBreakConfigSpan {
+  @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public final class LineBreakConfigSpan implements android.text.ParcelableSpan {
     ctor @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public LineBreakConfigSpan(@NonNull android.graphics.text.LineBreakConfig);
+    method @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") @NonNull public static android.text.style.LineBreakConfigSpan createNoBreakSpan();
+    method @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") @NonNull public static android.text.style.LineBreakConfigSpan createNoHyphenationSpan();
+    method public int describeContents();
     method @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") @NonNull public android.graphics.text.LineBreakConfig getLineBreakConfig();
-  }
-
-  @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public static final class LineBreakConfigSpan.NoBreakSpan extends android.text.style.LineBreakConfigSpan {
-    ctor @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public LineBreakConfigSpan.NoBreakSpan();
-  }
-
-  @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public static final class LineBreakConfigSpan.NoHyphenationSpan extends android.text.style.LineBreakConfigSpan {
-    ctor @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") public LineBreakConfigSpan.NoHyphenationSpan();
+    method public int getSpanTypeId();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.text.style.LineBreakConfigSpan> CREATOR;
   }
 
   public interface LineHeightSpan extends android.text.style.ParagraphStyle android.text.style.WrapTogetherSpan {
@@ -49803,6 +49805,7 @@
     method public boolean applyTransactionOnDraw(@NonNull android.view.SurfaceControl.Transaction);
     method @Nullable public android.view.SurfaceControl.Transaction buildReparentTransaction(@NonNull android.view.SurfaceControl);
     method public default int getBufferTransformHint();
+    method @FlaggedApi("com.android.window.flags.get_host_token_api") @Nullable public default android.os.IBinder getHostToken();
     method public default void removeOnBufferTransformHintChangedListener(@NonNull android.view.AttachedSurfaceControl.OnBufferTransformHintChangedListener);
     method public default void setChildBoundingInsets(@NonNull android.graphics.Rect);
     method public default void setTouchableRegion(@Nullable android.graphics.Region);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 0ad73af..183b925 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -37,7 +37,7 @@
     field public static final String ALLOW_ANY_CODEC_FOR_PLAYBACK = "android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK";
     field public static final String ALLOW_PLACE_IN_MULTI_PANE_SETTINGS = "android.permission.ALLOW_PLACE_IN_MULTI_PANE_SETTINGS";
     field public static final String ALLOW_SLIPPERY_TOUCHES = "android.permission.ALLOW_SLIPPERY_TOUCHES";
-    field public static final String ALWAYS_UPDATE_WALLPAPER = "android.permission.ALWAYS_UPDATE_WALLPAPER";
+    field @FlaggedApi("com.android.window.flags.always_update_wallpaper_permission") public static final String ALWAYS_UPDATE_WALLPAPER = "android.permission.ALWAYS_UPDATE_WALLPAPER";
     field public static final String AMBIENT_WALLPAPER = "android.permission.AMBIENT_WALLPAPER";
     field public static final String APPROVE_INCIDENT_REPORTS = "android.permission.APPROVE_INCIDENT_REPORTS";
     field public static final String ASSOCIATE_COMPANION_DEVICES = "android.permission.ASSOCIATE_COMPANION_DEVICES";
@@ -627,6 +627,7 @@
     field public static final String OPSTR_BIND_ACCESSIBILITY_SERVICE = "android:bind_accessibility_service";
     field public static final String OPSTR_CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD = "android:capture_consentless_bugreport_on_userdebug_build";
     field public static final String OPSTR_CHANGE_WIFI_STATE = "android:change_wifi_state";
+    field @FlaggedApi("android.view.contentprotection.flags.create_accessibility_overlay_app_op_enabled") public static final String OPSTR_CREATE_ACCESSIBILITY_OVERLAY = "android:create_accessibility_overlay";
     field public static final String OPSTR_ESTABLISH_VPN_MANAGER = "android:establish_vpn_manager";
     field public static final String OPSTR_ESTABLISH_VPN_SERVICE = "android:establish_vpn_service";
     field public static final String OPSTR_GET_ACCOUNTS = "android:get_accounts";
@@ -3819,7 +3820,7 @@
     field public static final String EXTRA_CALLBACK = "android.content.pm.extra.CALLBACK";
     field public static final String EXTRA_DATA_LOADER_TYPE = "android.content.pm.extra.DATA_LOADER_TYPE";
     field public static final String EXTRA_LEGACY_STATUS = "android.content.pm.extra.LEGACY_STATUS";
-    field public static final String EXTRA_RESOLVED_BASE_PATH = "android.content.pm.extra.RESOLVED_BASE_PATH";
+    field @Deprecated public static final String EXTRA_RESOLVED_BASE_PATH = "android.content.pm.extra.RESOLVED_BASE_PATH";
     field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_UNARCHIVE_ALL_USERS = "android.content.pm.extra.UNARCHIVE_ALL_USERS";
     field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_UNARCHIVE_PACKAGE_NAME = "android.content.pm.extra.UNARCHIVE_PACKAGE_NAME";
     field public static final int LOCATION_DATA_APP = 0; // 0x0
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 74a4440..a3ebe6e 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1082,7 +1082,7 @@
     method public android.os.UserHandle getUserHandle();
     method public boolean isAdmin();
     method public boolean isCloneProfile();
-    method public boolean isCommunalProfile();
+    method @FlaggedApi("android.multiuser.support_communal_profile") public boolean isCommunalProfile();
     method public boolean isDemo();
     method public boolean isEnabled();
     method public boolean isEphemeral();
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 9a19d8e..b03bd59 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -16,10 +16,13 @@
 
 package android.app;
 
+import static android.view.contentprotection.flags.Flags.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED;
+
 import static java.lang.Long.max;
 
 import android.Manifest;
 import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
@@ -1495,9 +1498,17 @@
     public static final int OP_RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA =
             AppProtoEnums.APP_OP_RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA;
 
+    /**
+     * Creation of an overlay using accessibility services
+     *
+     * @hide
+     */
+    public static final int OP_CREATE_ACCESSIBILITY_OVERLAY =
+            AppProtoEnums.APP_OP_CREATE_ACCESSIBILITY_OVERLAY;
+
     /** @hide */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public static final int _NUM_OP = 138;
+    public static final int _NUM_OP = 139;
 
     /**
      * All app ops represented as strings.
@@ -1641,7 +1652,8 @@
             OPSTR_CAMERA_SANDBOXED,
             OPSTR_RECORD_AUDIO_SANDBOXED,
             OPSTR_RECEIVE_SANDBOX_TRIGGER_AUDIO,
-            OPSTR_RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA
+            OPSTR_RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA,
+            OPSTR_CREATE_ACCESSIBILITY_OVERLAY,
     })
     public @interface AppOpString {}
 
@@ -2270,6 +2282,16 @@
     public static final String OPSTR_RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA =
             "android:RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA";
 
+    /**
+     * Creation of an overlay using accessibility services
+     *
+     * @hide
+     */
+    @SystemApi
+    @FlaggedApi(FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED)
+    public static final String OPSTR_CREATE_ACCESSIBILITY_OVERLAY =
+            "android:create_accessibility_overlay";
+
     /** {@link #sAppOpsToNote} not initialized yet for this op */
     private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0;
     /** Should not collect noting of this app-op in {@link #sAppOpsToNote} */
@@ -2819,7 +2841,11 @@
                 OPSTR_RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA,
                 "RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA")
                 .setPermission(Manifest.permission.RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA)
-                .setDefaultMode(AppOpsManager.MODE_DEFAULT).build()
+                .setDefaultMode(AppOpsManager.MODE_DEFAULT).build(),
+        new AppOpInfo.Builder(OP_CREATE_ACCESSIBILITY_OVERLAY,
+                OPSTR_CREATE_ACCESSIBILITY_OVERLAY,
+                "CREATE_ACCESSIBILITY_OVERLAY")
+                .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
     };
 
     // The number of longs needed to form a full bitmask of app ops
@@ -8305,7 +8331,9 @@
      */
     public int unsafeCheckOpRawNoThrow(int op, int uid, @NonNull String packageName) {
         try {
-            return mService.checkOperationRaw(op, uid, packageName, null);
+            final AttributionSource attributionSource =
+                    new AttributionSource.Builder(uid).setPackageName(packageName).build();
+            return mService.checkOperationWithStateRaw(op, attributionSource.asState());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -8468,7 +8496,12 @@
                 }
             }
 
-            SyncNotedAppOp syncOp = mService.noteOperation(op, uid, packageName, attributionTag,
+            final AttributionSource attributionSource =
+                    new AttributionSource.Builder(uid)
+                            .setPackageName(packageName)
+                            .setAttributionTag(attributionTag)
+                            .build();
+            SyncNotedAppOp syncOp = mService.noteOperationWithState(op, attributionSource.asState(),
                     collectionMode == COLLECT_ASYNC, message, shouldCollectMessage);
 
             if (syncOp.getOpMode() == MODE_ALLOWED) {
@@ -8708,7 +8741,9 @@
     @UnsupportedAppUsage
     public int checkOp(int op, int uid, String packageName) {
         try {
-            int mode = mService.checkOperation(op, uid, packageName);
+            final AttributionSource attributionSource =
+                    new AttributionSource.Builder(uid).setPackageName(packageName).build();
+            int mode = mService.checkOperationWithState(op, attributionSource.asState());
             if (mode == MODE_ERRORED) {
                 throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
             }
@@ -8729,7 +8764,9 @@
     @UnsupportedAppUsage
     public int checkOpNoThrow(int op, int uid, String packageName) {
         try {
-            int mode = mService.checkOperation(op, uid, packageName);
+            final AttributionSource attributionSource =
+                    new AttributionSource.Builder(uid).setPackageName(packageName).build();
+            int mode = mService.checkOperationWithState(op, attributionSource.asState());
             return mode == AppOpsManager.MODE_FOREGROUND ? AppOpsManager.MODE_ALLOWED : mode;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -8974,8 +9011,14 @@
                 }
             }
 
-            SyncNotedAppOp syncOp = mService.startOperation(token, op, uid, packageName,
-                    attributionTag, startIfModeDefault, collectionMode == COLLECT_ASYNC, message,
+            final AttributionSource attributionSource =
+                    new AttributionSource.Builder(uid)
+                            .setPackageName(packageName)
+                            .setAttributionTag(attributionTag)
+                            .build();
+            SyncNotedAppOp syncOp = mService.startOperationWithState(token, op,
+                    attributionSource.asState(), startIfModeDefault,
+                    collectionMode == COLLECT_ASYNC, message,
                     shouldCollectMessage, attributionFlags, attributionChainId);
 
             if (syncOp.getOpMode() == MODE_ALLOWED) {
@@ -9188,7 +9231,12 @@
     public void finishOp(IBinder token, int op, int uid, @NonNull String packageName,
             @Nullable String attributionTag) {
         try {
-            mService.finishOperation(token, op, uid, packageName, attributionTag);
+            final AttributionSource attributionSource =
+                    new AttributionSource.Builder(uid)
+                            .setPackageName(packageName)
+                            .setAttributionTag(attributionTag)
+                            .build();
+            mService.finishOperationWithState(token, op, attributionSource.asState());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/AppOpsManagerInternal.java b/core/java/android/app/AppOpsManagerInternal.java
index 43023fe..a3de8fa 100644
--- a/core/java/android/app/AppOpsManagerInternal.java
+++ b/core/java/android/app/AppOpsManagerInternal.java
@@ -26,11 +26,12 @@
 import android.util.SparseIntArray;
 
 import com.android.internal.app.IAppOpsCallback;
-import com.android.internal.util.function.HeptFunction;
 import com.android.internal.util.function.HexFunction;
+import com.android.internal.util.function.NonaFunction;
 import com.android.internal.util.function.QuadFunction;
-import com.android.internal.util.function.QuintConsumer;
 import com.android.internal.util.function.QuintFunction;
+import com.android.internal.util.function.TriConsumer;
+import com.android.internal.util.function.TriFunction;
 import com.android.internal.util.function.UndecFunction;
 
 /**
@@ -45,15 +46,13 @@
          * Allows overriding check operation behavior.
          *
          * @param code The op code to check.
-         * @param uid The UID for which to check.
-         * @param packageName The package for which to check.
-         * @param attributionTag The attribution tag for which to check.
+         * @param attributionSource the {@link AttributionSource} responsible for data access
          * @param raw Whether to check the raw op i.e. not interpret the mode based on UID state.
          * @param superImpl The super implementation.
          * @return The app op check result.
          */
-        int checkOperation(int code, int uid, String packageName, @Nullable String attributionTag,
-                boolean raw, QuintFunction<Integer, Integer, String, String, Boolean, Integer>
+        int checkOperation(int code, AttributionSource attributionSource,
+                boolean raw, TriFunction<Integer, AttributionSource, Boolean, Integer>
                 superImpl);
 
         /**
@@ -73,25 +72,23 @@
          * Allows overriding note operation behavior.
          *
          * @param code The op code to note.
-         * @param uid The UID for which to note.
-         * @param packageName The package for which to note. {@code null} for system package.
-         * @param featureId Id of the feature in the package
+         * @param attributionSource the {@link AttributionSource} responsible for data access
          * @param shouldCollectAsyncNotedOp If an {@link AsyncNotedAppOp} should be collected
          * @param message The message in the async noted op
          * @param superImpl The super implementation.
          * @return The app op note result.
          */
-        SyncNotedAppOp noteOperation(int code, int uid, @Nullable String packageName,
-                @Nullable String featureId, boolean shouldCollectAsyncNotedOp,
+        SyncNotedAppOp noteOperation(int code, AttributionSource attributionSource,
+                boolean shouldCollectAsyncNotedOp,
                 @Nullable String message, boolean shouldCollectMessage,
-                @NonNull HeptFunction<Integer, Integer, String, String, Boolean, String, Boolean,
+                @NonNull QuintFunction<Integer, AttributionSource, Boolean, String, Boolean,
                         SyncNotedAppOp> superImpl);
 
         /**
          * Allows overriding note proxy operation behavior.
          *
          * @param code The op code to note.
-         * @param attributionSource The permission identity of the caller.
+         * @param attributionSource the {@link AttributionSource} responsible for data access
          * @param shouldCollectAsyncNotedOp If an {@link AsyncNotedAppOp} should be collected
          * @param message The message in the async noted op
          * @param shouldCollectMessage whether to collect messages
@@ -110,9 +107,7 @@
          *
          * @param token The client state.
          * @param code The op code to start.
-         * @param uid The UID for which to note.
-         * @param packageName The package for which to note. {@code null} for system package.
-         * @param attributionTag the attribution tag.
+         * @param attributionSource the {@link AttributionSource} responsible for data access
          * @param startIfModeDefault Whether to start the op of the mode is default.
          * @param shouldCollectAsyncNotedOp If an {@link AsyncNotedAppOp} should be collected
          * @param message The message in the async noted op
@@ -122,12 +117,12 @@
          * @param superImpl The super implementation.
          * @return The app op note result.
          */
-        SyncNotedAppOp startOperation(IBinder token, int code, int uid,
-                @Nullable String packageName, @Nullable String attributionTag,
+        SyncNotedAppOp startOperation(IBinder token, int code,
+                AttributionSource attributionSource,
                 boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp,
                 @Nullable String message, boolean shouldCollectMessage,
                 @AttributionFlags int attributionFlags, int attributionChainId,
-                @NonNull UndecFunction<IBinder, Integer, Integer, String, String, Boolean,
+                @NonNull NonaFunction<IBinder, Integer, AttributionSource, Boolean,
                         Boolean, String, Boolean, Integer, Integer, SyncNotedAppOp> superImpl);
 
         /**
@@ -135,7 +130,7 @@
          *
          * @param clientId The client calling start, represented by an IBinder
          * @param code The op code to start.
-         * @param attributionSource The permission identity of the caller.
+         * @param attributionSource the {@link AttributionSource} responsible for data access
          * @param startIfModeDefault Whether to start the op of the mode is default.
          * @param shouldCollectAsyncNotedOp If an {@link AsyncNotedAppOp} should be collected
          * @param message The message in the async noted op
@@ -161,21 +156,19 @@
          *
          * @param clientId The client state.
          * @param code The op code to finish.
-         * @param uid The UID for which the op was noted.
-         * @param packageName The package for which it was noted. {@code null} for system package.
-         * @param attributionTag the attribution tag.
+         * @param attributionSource the {@link AttributionSource} responsible for data access
          */
-        default void finishOperation(IBinder clientId, int code, int uid, String packageName,
-                String attributionTag,
-                @NonNull QuintConsumer<IBinder, Integer, Integer, String, String> superImpl) {
-            superImpl.accept(clientId, code, uid, packageName, attributionTag);
+        default void finishOperation(IBinder clientId, int code,
+                AttributionSource attributionSource,
+                @NonNull TriConsumer<IBinder, Integer, AttributionSource> superImpl) {
+            superImpl.accept(clientId, code, attributionSource);
         }
 
         /**
          * Allows overriding finish proxy op.
          *
          * @param code The op code to finish.
-         * @param attributionSource The permission identity of the caller.
+         * @param attributionSource the {@link AttributionSource} responsible for data access
          * @param skipProxyOperation Whether to skip the proxy in the proxy/proxied operation
          * @param clientId The client calling finishProxyOperation
          * @param superImpl The "standard" implementation to potentially call
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index fd308ce..367e92b 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -48,7 +48,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApkChecksum;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.ArchivedPackage;
+import android.content.pm.ArchivedPackageInfo;
 import android.content.pm.ChangedPackages;
 import android.content.pm.Checksum;
 import android.content.pm.ComponentInfo;
@@ -3937,13 +3937,13 @@
     }
 
     @Override
-    public @Nullable ArchivedPackage getArchivedPackage(@NonNull String packageName) {
+    public @Nullable ArchivedPackageInfo getArchivedPackage(@NonNull String packageName) {
         try {
             var parcel = mPM.getArchivedPackage(packageName, mContext.getUserId());
             if (parcel == null) {
                 return null;
             }
-            return new ArchivedPackage(parcel);
+            return new ArchivedPackageInfo(parcel);
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
new file mode 100644
index 0000000..cd1d8ce
--- /dev/null
+++ b/core/java/android/app/notification.aconfig
@@ -0,0 +1,8 @@
+package: "android.app"
+
+flag {
+  name: "modes_api"
+  namespace: "systemui"
+  description: "This flag controls new and updated DND apis"
+  bug: "300477976"
+}
\ No newline at end of file
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 3abe1d9..c6012bb 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -562,13 +562,6 @@
         });
     }
 
-    private boolean isPostingTaskToBackground(@Nullable RemoteViews views) {
-        return Looper.myLooper() == Looper.getMainLooper()
-                && RemoteViews.isAdapterConversionEnabled()
-                && (mHasPostedLegacyLists = mHasPostedLegacyLists
-                        || (views != null && views.hasLegacyLists()));
-    }
-
     /**
      * Set the RemoteViews to use for the specified appWidgetIds.
      * <p>
@@ -593,16 +586,25 @@
             return;
         }
 
-        if (isPostingTaskToBackground(views)) {
-            createUpdateExecutorIfNull().execute(() -> {
-                try {
-                    mService.updateAppWidgetIds(mPackageName, appWidgetIds, views);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Error updating app widget views in background", e);
-                }
-            });
+        final boolean isConvertingAdapter = RemoteViews.isAdapterConversionEnabled()
+                && (mHasPostedLegacyLists = mHasPostedLegacyLists
+                        || (views != null && views.hasLegacyLists()));
 
-            return;
+        if (isConvertingAdapter) {
+            views.collectAllIntents();
+
+            if (Looper.getMainLooper() == Looper.myLooper()) {
+                RemoteViews viewsCopy = new RemoteViews(views);
+                createUpdateExecutorIfNull().execute(() -> {
+                    try {
+                        mService.updateAppWidgetIds(mPackageName, appWidgetIds, viewsCopy);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Error updating app widget views in background", e);
+                    }
+                });
+
+                return;
+            }
         }
 
         try {
@@ -714,16 +716,25 @@
             return;
         }
 
-        if (isPostingTaskToBackground(views)) {
-            createUpdateExecutorIfNull().execute(() -> {
-                try {
-                    mService.partiallyUpdateAppWidgetIds(mPackageName, appWidgetIds, views);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Error partially updating app widget views in background", e);
-                }
-            });
+        final boolean isConvertingAdapter = RemoteViews.isAdapterConversionEnabled()
+                && (mHasPostedLegacyLists = mHasPostedLegacyLists
+                        || (views != null && views.hasLegacyLists()));
 
-            return;
+        if (isConvertingAdapter) {
+            views.collectAllIntents();
+
+            if (Looper.getMainLooper() == Looper.myLooper()) {
+                RemoteViews viewsCopy = new RemoteViews(views);
+                createUpdateExecutorIfNull().execute(() -> {
+                    try {
+                        mService.partiallyUpdateAppWidgetIds(mPackageName, appWidgetIds, viewsCopy);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Error partially updating app widget views in background", e);
+                    }
+                });
+
+                return;
+            }
         }
 
         try {
@@ -782,16 +793,26 @@
             return;
         }
 
-        if (isPostingTaskToBackground(views)) {
-            createUpdateExecutorIfNull().execute(() -> {
-                try {
-                    mService.updateAppWidgetProvider(provider, views);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Error updating app widget view using provider in background", e);
-                }
-            });
+        final boolean isConvertingAdapter = RemoteViews.isAdapterConversionEnabled()
+                && (mHasPostedLegacyLists = mHasPostedLegacyLists
+                        || (views != null && views.hasLegacyLists()));
 
-            return;
+        if (isConvertingAdapter) {
+            views.collectAllIntents();
+
+            if (Looper.getMainLooper() == Looper.myLooper()) {
+                RemoteViews viewsCopy = new RemoteViews(views);
+                createUpdateExecutorIfNull().execute(() -> {
+                    try {
+                        mService.updateAppWidgetProvider(provider, viewsCopy);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Error updating app widget view using provider in background",
+                                e);
+                    }
+                });
+
+                return;
+            }
         }
 
         try {
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index d40a591..b3ea93b 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -185,9 +185,6 @@
             int associationId,
             @NonNull VirtualDeviceParams params) {
         Objects.requireNonNull(params, "params must not be null");
-        if (Flags.moreLogs()) {
-            Log.i(TAG, "Creating VirtualDevice");
-        }
         try {
             return new VirtualDevice(mService, mContext, associationId, params);
         } catch (RemoteException e) {
diff --git a/core/java/android/companion/virtual/flags.aconfig b/core/java/android/companion/virtual/flags.aconfig
index f380963..3cadb7c 100644
--- a/core/java/android/companion/virtual/flags.aconfig
+++ b/core/java/android/companion/virtual/flags.aconfig
@@ -1,13 +1,6 @@
 package: "android.companion.virtual.flags"
 
 flag {
-  name: "more_logs"
-  namespace: "virtual_devices"
-  description: "More logs to test flags with"
-  bug: "291725823"
-}
-
-flag {
   name: "enable_native_vdm"
   namespace: "virtual_devices"
   description: "Enable native VDM service"
diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java
index 4b2cee6..c2bc974 100644
--- a/core/java/android/content/AttributionSource.java
+++ b/core/java/android/content/AttributionSource.java
@@ -235,6 +235,12 @@
     }
 
     /** @hide */
+    public AttributionSource withUid(int uid) {
+        return new AttributionSource(uid, getPid(), getPackageName(), getAttributionTag(),
+                getToken(), mAttributionSourceState.renouncedPermissions, getDeviceId(), getNext());
+    }
+
+    /** @hide */
     public AttributionSource withPid(int pid) {
         return new AttributionSource(getUid(), pid, getPackageName(), getAttributionTag(),
                 getToken(), mAttributionSourceState.renouncedPermissions, getDeviceId(), getNext());
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index ffc4805..ea54c91 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1164,6 +1164,11 @@
      * numbers.  Applications can <strong>dial</strong> emergency numbers using
      * {@link #ACTION_DIAL}, however.
      *
+     * <p>Note: This Intent can only be used to dial call forwarding MMI codes if the application
+     * using this intent is set as the default or system dialer. The system will treat any other
+     * application using this Intent for the purpose of dialing call forwarding MMI codes as if the
+     * {@link #ACTION_DIAL} Intent was used instead.
+     *
      * <p>Note: An app filling the {@link android.app.role.RoleManager#ROLE_DIALER} role should use
      * {@link android.telecom.TelecomManager#placeCall(Uri, Bundle)} to place calls rather than
      * relying on this intent.
diff --git a/core/java/android/content/pm/ArchivedActivity.java b/core/java/android/content/pm/ArchivedActivityInfo.java
similarity index 89%
rename from core/java/android/content/pm/ArchivedActivity.java
rename to core/java/android/content/pm/ArchivedActivityInfo.java
index 9e49c9e..1faa437 100644
--- a/core/java/android/content/pm/ArchivedActivity.java
+++ b/core/java/android/content/pm/ArchivedActivityInfo.java
@@ -32,9 +32,13 @@
 import java.io.IOException;
 import java.util.Objects;
 
+/**
+ * Contains fields required to show archived package in Launcher.
+ * @see ArchivedPackageInfo
+ */
 @DataClass(genBuilder = false, genConstructor = false, genSetters = true)
 @FlaggedApi(Flags.FLAG_ARCHIVING)
-public final class ArchivedActivity {
+public final class ArchivedActivityInfo {
     /** The label for the activity. */
     private @NonNull CharSequence mLabel;
     /** The component name of this activity. */
@@ -47,7 +51,7 @@
     /** Monochrome icon, if defined, of the activity. */
     private @Nullable Drawable mMonochromeIcon;
 
-    public ArchivedActivity(@NonNull CharSequence label, @NonNull ComponentName componentName) {
+    public ArchivedActivityInfo(@NonNull CharSequence label, @NonNull ComponentName componentName) {
         Objects.requireNonNull(label);
         Objects.requireNonNull(componentName);
         mLabel = label;
@@ -55,7 +59,7 @@
     }
 
     /* @hide */
-    ArchivedActivity(@NonNull ArchivedActivityParcel parcel) {
+    ArchivedActivityInfo(@NonNull ArchivedActivityParcel parcel) {
         mLabel = parcel.title;
         mComponentName = parcel.originalComponentName;
         mIcon = drawableFromCompressedBitmap(parcel.iconBitmap);
@@ -149,7 +153,7 @@
     // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/ArchivedActivity.java
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/ArchivedActivityInfo.java
     //
     // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
     //   Settings > Editor > Code Style > Formatter Control
@@ -193,7 +197,7 @@
      * The label for the activity.
      */
     @DataClass.Generated.Member
-    public @NonNull ArchivedActivity setLabel(@NonNull CharSequence value) {
+    public @NonNull ArchivedActivityInfo setLabel(@NonNull CharSequence value) {
         mLabel = value;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mLabel);
@@ -204,7 +208,7 @@
      * The component name of this activity.
      */
     @DataClass.Generated.Member
-    public @NonNull ArchivedActivity setComponentName(@NonNull ComponentName value) {
+    public @NonNull ArchivedActivityInfo setComponentName(@NonNull ComponentName value) {
         mComponentName = value;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mComponentName);
@@ -216,7 +220,7 @@
      * launcher.
      */
     @DataClass.Generated.Member
-    public @NonNull ArchivedActivity setIcon(@NonNull Drawable value) {
+    public @NonNull ArchivedActivityInfo setIcon(@NonNull Drawable value) {
         mIcon = value;
         return this;
     }
@@ -225,16 +229,16 @@
      * Monochrome icon, if defined, of the activity.
      */
     @DataClass.Generated.Member
-    public @NonNull ArchivedActivity setMonochromeIcon(@NonNull Drawable value) {
+    public @NonNull ArchivedActivityInfo setMonochromeIcon(@NonNull Drawable value) {
         mMonochromeIcon = value;
         return this;
     }
 
     @DataClass.Generated(
-            time = 1698173429911L,
+            time = 1698789991876L,
             codegenVersion = "1.0.23",
-            sourceFile = "frameworks/base/core/java/android/content/pm/ArchivedActivity.java",
-            inputSignatures = "private @android.annotation.NonNull java.lang.CharSequence mLabel\nprivate @android.annotation.NonNull android.content.ComponentName mComponentName\nprivate @android.annotation.Nullable android.graphics.drawable.Drawable mIcon\nprivate @android.annotation.Nullable android.graphics.drawable.Drawable mMonochromeIcon\n @android.annotation.NonNull android.content.pm.ArchivedActivityParcel getParcel()\npublic static  android.graphics.Bitmap drawableToBitmap(android.graphics.drawable.Drawable)\npublic static  android.graphics.Bitmap drawableToBitmap(android.graphics.drawable.Drawable,int)\npublic static  byte[] bytesFromBitmap(android.graphics.Bitmap)\nprivate static  android.graphics.drawable.Drawable drawableFromCompressedBitmap(byte[])\nclass ArchivedActivity extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=false, genConstructor=false, genSetters=true)")
+            sourceFile = "frameworks/base/core/java/android/content/pm/ArchivedActivityInfo.java",
+            inputSignatures = "private @android.annotation.NonNull java.lang.CharSequence mLabel\nprivate @android.annotation.NonNull android.content.ComponentName mComponentName\nprivate @android.annotation.Nullable android.graphics.drawable.Drawable mIcon\nprivate @android.annotation.Nullable android.graphics.drawable.Drawable mMonochromeIcon\n @android.annotation.NonNull android.content.pm.ArchivedActivityParcel getParcel()\npublic static  android.graphics.Bitmap drawableToBitmap(android.graphics.drawable.Drawable)\npublic static  android.graphics.Bitmap drawableToBitmap(android.graphics.drawable.Drawable,int)\npublic static  byte[] bytesFromBitmap(android.graphics.Bitmap)\nprivate static  android.graphics.drawable.Drawable drawableFromCompressedBitmap(byte[])\nclass ArchivedActivityInfo extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=false, genConstructor=false, genSetters=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/content/pm/ArchivedPackage.java b/core/java/android/content/pm/ArchivedPackageInfo.java
similarity index 86%
rename from core/java/android/content/pm/ArchivedPackage.java
rename to core/java/android/content/pm/ArchivedPackageInfo.java
index 42795db..f432598 100644
--- a/core/java/android/content/pm/ArchivedPackage.java
+++ b/core/java/android/content/pm/ArchivedPackageInfo.java
@@ -27,9 +27,13 @@
 import java.util.List;
 import java.util.Objects;
 
+/**
+ * Contains fields required for archived package installation,
+ * i.e. installation without an APK.
+ */
 @DataClass(genBuilder = false, genConstructor = false, genSetters = true)
 @FlaggedApi(Flags.FLAG_ARCHIVING)
-public final class ArchivedPackage {
+public final class ArchivedPackageInfo {
     /** Name of the package as used to identify it in the system */
     private @NonNull String mPackageName;
     /** Signing certificates used to sign the package. */
@@ -74,10 +78,10 @@
      * {@link Intent#CATEGORY_LAUNCHER}.
      * @see LauncherApps#getActivityList
      */
-    private @NonNull List<ArchivedActivity> mLauncherActivities;
+    private @NonNull List<ArchivedActivityInfo> mLauncherActivities;
 
-    public ArchivedPackage(@NonNull String packageName, @NonNull SigningInfo signingInfo,
-            @NonNull List<ArchivedActivity> launcherActivities) {
+    public ArchivedPackageInfo(@NonNull String packageName, @NonNull SigningInfo signingInfo,
+            @NonNull List<ArchivedActivityInfo> launcherActivities) {
         Objects.requireNonNull(packageName);
         Objects.requireNonNull(signingInfo);
         Objects.requireNonNull(launcherActivities);
@@ -90,7 +94,7 @@
      * Constructs the archived package from parcel.
      * @hide
      */
-    public ArchivedPackage(@NonNull ArchivedPackageParcel parcel) {
+    public ArchivedPackageInfo(@NonNull ArchivedPackageParcel parcel) {
         mPackageName = parcel.packageName;
         mSigningInfo = new SigningInfo(parcel.signingDetails);
         mVersionCode = parcel.versionCode;
@@ -102,7 +106,7 @@
         mLauncherActivities = new ArrayList<>();
         if (parcel.archivedActivities != null) {
             for (var activityParcel : parcel.archivedActivities) {
-                mLauncherActivities.add(new ArchivedActivity(activityParcel));
+                mLauncherActivities.add(new ArchivedActivityInfo(activityParcel));
             }
         }
     }
@@ -135,7 +139,7 @@
     // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/ArchivedPackage.java
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/ArchivedPackageInfo.java
     //
     // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
     //   Settings > Editor > Code Style > Formatter Control
@@ -224,7 +228,7 @@
      * @see LauncherApps#getActivityList
      */
     @DataClass.Generated.Member
-    public @NonNull List<ArchivedActivity> getLauncherActivities() {
+    public @NonNull List<ArchivedActivityInfo> getLauncherActivities() {
         return mLauncherActivities;
     }
 
@@ -232,7 +236,7 @@
      * Name of the package as used to identify it in the system
      */
     @DataClass.Generated.Member
-    public @NonNull ArchivedPackage setPackageName(@NonNull String value) {
+    public @NonNull ArchivedPackageInfo setPackageName(@NonNull String value) {
         mPackageName = value;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mPackageName);
@@ -243,7 +247,7 @@
      * Signing certificates used to sign the package.
      */
     @DataClass.Generated.Member
-    public @NonNull ArchivedPackage setSigningInfo(@NonNull SigningInfo value) {
+    public @NonNull ArchivedPackageInfo setSigningInfo(@NonNull SigningInfo value) {
         mSigningInfo = value;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mSigningInfo);
@@ -255,7 +259,7 @@
      * {@link android.R.styleable#AndroidManifest_versionCode versionCode} attribute.
      */
     @DataClass.Generated.Member
-    public @NonNull ArchivedPackage setVersionCode( int value) {
+    public @NonNull ArchivedPackageInfo setVersionCode( int value) {
         mVersionCode = value;
         return this;
     }
@@ -265,7 +269,7 @@
      * {@link android.R.styleable#AndroidManifest_versionCode versionCodeMajor} attribute.
      */
     @DataClass.Generated.Member
-    public @NonNull ArchivedPackage setVersionCodeMajor( int value) {
+    public @NonNull ArchivedPackageInfo setVersionCodeMajor( int value) {
         mVersionCodeMajor = value;
         return this;
     }
@@ -276,7 +280,7 @@
      * attribute.
      */
     @DataClass.Generated.Member
-    public @NonNull ArchivedPackage setTargetSdkVersion( int value) {
+    public @NonNull ArchivedPackageInfo setTargetSdkVersion( int value) {
         mTargetSdkVersion = value;
         return this;
     }
@@ -287,7 +291,7 @@
      * attribute.
      */
     @DataClass.Generated.Member
-    public @NonNull ArchivedPackage setDefaultToDeviceProtectedStorage(@NonNull String value) {
+    public @NonNull ArchivedPackageInfo setDefaultToDeviceProtectedStorage(@NonNull String value) {
         mDefaultToDeviceProtectedStorage = value;
         return this;
     }
@@ -299,7 +303,7 @@
      * attribute.
      */
     @DataClass.Generated.Member
-    public @NonNull ArchivedPackage setRequestLegacyExternalStorage(@NonNull String value) {
+    public @NonNull ArchivedPackageInfo setRequestLegacyExternalStorage(@NonNull String value) {
         mRequestLegacyExternalStorage = value;
         return this;
     }
@@ -310,7 +314,7 @@
      * {@link android.R.styleable#AndroidManifestApplication_hasFragileUserData} attribute.
      */
     @DataClass.Generated.Member
-    public @NonNull ArchivedPackage setUserDataFragile(@NonNull String value) {
+    public @NonNull ArchivedPackageInfo setUserDataFragile(@NonNull String value) {
         mUserDataFragile = value;
         return this;
     }
@@ -322,7 +326,7 @@
      * @see LauncherApps#getActivityList
      */
     @DataClass.Generated.Member
-    public @NonNull ArchivedPackage setLauncherActivities(@NonNull List<ArchivedActivity> value) {
+    public @NonNull ArchivedPackageInfo setLauncherActivities(@NonNull List<ArchivedActivityInfo> value) {
         mLauncherActivities = value;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mLauncherActivities);
@@ -330,10 +334,10 @@
     }
 
     @DataClass.Generated(
-            time = 1697824890503L,
+            time = 1698789995536L,
             codegenVersion = "1.0.23",
-            sourceFile = "frameworks/base/core/java/android/content/pm/ArchivedPackage.java",
-            inputSignatures = "private @android.annotation.NonNull java.lang.String mPackageName\nprivate @android.annotation.NonNull android.content.pm.SigningInfo mSigningInfo\nprivate  int mVersionCode\nprivate  int mVersionCodeMajor\nprivate  int mTargetSdkVersion\nprivate @android.annotation.Nullable java.lang.String mDefaultToDeviceProtectedStorage\nprivate @android.annotation.Nullable java.lang.String mRequestLegacyExternalStorage\nprivate @android.annotation.Nullable java.lang.String mUserDataFragile\nprivate @android.annotation.NonNull java.util.List<android.content.pm.ArchivedActivity> mLauncherActivities\n  android.content.pm.ArchivedPackageParcel getParcel()\nclass ArchivedPackage extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=false, genConstructor=false, genSetters=true)")
+            sourceFile = "frameworks/base/core/java/android/content/pm/ArchivedPackageInfo.java",
+            inputSignatures = "private @android.annotation.NonNull java.lang.String mPackageName\nprivate @android.annotation.NonNull android.content.pm.SigningInfo mSigningInfo\nprivate  int mVersionCode\nprivate  int mVersionCodeMajor\nprivate  int mTargetSdkVersion\nprivate @android.annotation.Nullable java.lang.String mDefaultToDeviceProtectedStorage\nprivate @android.annotation.Nullable java.lang.String mRequestLegacyExternalStorage\nprivate @android.annotation.Nullable java.lang.String mUserDataFragile\nprivate @android.annotation.NonNull java.util.List<android.content.pm.ArchivedActivityInfo> mLauncherActivities\n  android.content.pm.ArchivedPackageParcel getParcel()\nclass ArchivedPackageInfo extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=false, genConstructor=false, genSetters=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index cbb20e0..1114b35 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -343,7 +343,10 @@
      * point at the existing base APK (when adding splits to an existing app).
      *
      * @hide
+     * @deprecated Resolved base path of an install session should not be available to unauthorized
+     * callers. Use {@link SessionInfo#getResolvedBaseApkPath()} instead.
      */
+    @Deprecated
     @SystemApi
     public static final String EXTRA_RESOLVED_BASE_PATH =
             "android.content.pm.extra.RESOLVED_BASE_PATH";
@@ -1003,7 +1006,7 @@
     /**
      * Install package in an archived state.
      *
-     * @param archivedPackage archived package data such as package name, signature etc.
+     * @param archivedPackageInfo archived package data such as package name, signature etc.
      * @param sessionParams used to create an underlying installation session
      * @param statusReceiver Called when the state of the session changes. Intents
      *                       sent to this receiver contain {@link #EXTRA_STATUS}. Refer to the
@@ -1013,15 +1016,15 @@
      */
     @RequiresPermission(Manifest.permission.INSTALL_PACKAGES)
     @FlaggedApi(Flags.FLAG_ARCHIVING)
-    public void installPackageArchived(@NonNull ArchivedPackage archivedPackage,
+    public void installPackageArchived(@NonNull ArchivedPackageInfo archivedPackageInfo,
             @NonNull SessionParams sessionParams,
             @NonNull IntentSender statusReceiver) {
-        Objects.requireNonNull(archivedPackage, "archivedPackage cannot be null");
+        Objects.requireNonNull(archivedPackageInfo, "archivedPackageInfo cannot be null");
         Objects.requireNonNull(sessionParams, "sessionParams cannot be null");
         Objects.requireNonNull(statusReceiver, "statusReceiver cannot be null");
         try {
             mInstaller.installPackageArchived(
-                    archivedPackage.getParcel(),
+                    archivedPackageInfo.getParcel(),
                     sessionParams,
                     statusReceiver,
                     mInstallerPackageName,
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index ad7dd51..dea4a12 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -11032,7 +11032,7 @@
      * @see PackageInstaller#installPackageArchived
      */
     @FlaggedApi(android.content.pm.Flags.FLAG_ARCHIVING)
-    public @Nullable ArchivedPackage getArchivedPackage(@NonNull String packageName) {
+    public @Nullable ArchivedPackageInfo getArchivedPackage(@NonNull String packageName) {
         throw new UnsupportedOperationException(
                 "getArchivedPackage not implemented in subclass");
     }
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index be586c3..8fd78bd 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -16,8 +16,6 @@
 
 package android.content.pm;
 
-import static android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE;
-
 import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -389,11 +387,12 @@
         return UserManager.isUserTypeCloneProfile(userType);
     }
 
+    @FlaggedApi(android.multiuser.Flags.FLAG_SUPPORT_COMMUNAL_PROFILE)
     public boolean isCommunalProfile() {
         return UserManager.isUserTypeCommunalProfile(userType);
     }
 
-    @FlaggedApi(FLAG_ALLOW_PRIVATE_PROFILE)
+    @FlaggedApi(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE)
     public boolean isPrivateProfile() {
         return UserManager.isUserTypePrivateProfile(userType);
     }
diff --git a/core/java/android/content/res/StringBlock.java b/core/java/android/content/res/StringBlock.java
index c143acb..0070a6f 100644
--- a/core/java/android/content/res/StringBlock.java
+++ b/core/java/android/content/res/StringBlock.java
@@ -24,6 +24,7 @@
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.Typeface;
+import android.graphics.text.LineBreakConfig;
 import android.text.Annotation;
 import android.text.Spannable;
 import android.text.SpannableString;
@@ -35,6 +36,7 @@
 import android.text.style.BulletSpan;
 import android.text.style.CharacterStyle;
 import android.text.style.ForegroundColorSpan;
+import android.text.style.LineBreakConfigSpan;
 import android.text.style.LineHeightSpan;
 import android.text.style.RelativeSizeSpan;
 import android.text.style.StrikethroughSpan;
@@ -176,6 +178,10 @@
                         mStyleIDs.listItemId = styleId;
                     } else if (styleTag.equals("marquee")) {
                         mStyleIDs.marqueeId = styleId;
+                    } else if (styleTag.equals("nobreak")) {
+                        mStyleIDs.mNoBreakId = styleId;
+                    } else if (styleTag.equals("nohyphen")) {
+                        mStyleIDs.mNoHyphenId = styleId;
                     }
                 }
 
@@ -224,6 +230,8 @@
         private int strikeId = -1;
         private int listItemId = -1;
         private int marqueeId = -1;
+        private int mNoBreakId = -1;
+        private int mNoHyphenId = -1;
     }
 
     @Nullable
@@ -285,12 +293,19 @@
                 buffer.setSpan(TextUtils.TruncateAt.MARQUEE,
                                style[i+1], style[i+2]+1,
                                Spannable.SPAN_INCLUSIVE_INCLUSIVE);
+            } else if (type == ids.mNoBreakId) {
+                buffer.setSpan(LineBreakConfigSpan.createNoBreakSpan(),
+                        style[i + 1], style[i + 2] + 1,
+                        Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
+            } else if (type == ids.mNoHyphenId) {
+                buffer.setSpan(LineBreakConfigSpan.createNoHyphenationSpan(),
+                        style[i + 1], style[i + 2] + 1,
+                        Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
             } else {
                 String tag = nativeGetString(mNative, type);
                 if (tag == null) {
                     return null;
                 }
-
                 if (tag.startsWith("font;")) {
                     String sub;
 
@@ -367,6 +382,44 @@
                                        style[i+1], style[i+2]+1,
                                        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                     }
+                } else if (tag.startsWith("lineBreakConfig;")) {
+                    String lbStyleStr = subtag(tag, ";style=");
+                    int lbStyle = LineBreakConfig.LINE_BREAK_STYLE_UNSPECIFIED;
+                    if (lbStyleStr != null) {
+                        if (lbStyleStr.equals("none")) {
+                            lbStyle = LineBreakConfig.LINE_BREAK_STYLE_NONE;
+                        } else if (lbStyleStr.equals("normal")) {
+                            lbStyle = LineBreakConfig.LINE_BREAK_STYLE_NORMAL;
+                        } else if (lbStyleStr.equals("loose")) {
+                            lbStyle = LineBreakConfig.LINE_BREAK_STYLE_LOOSE;
+                        } else if (lbStyleStr.equals("strict")) {
+                            lbStyle = LineBreakConfig.LINE_BREAK_STYLE_STRICT;
+                        } else {
+                            Log.w(TAG, "Unknown LineBreakConfig style: " + lbStyleStr);
+                        }
+                    }
+
+                    String lbWordStyleStr = subtag(tag, ";wordStyle=");
+                    int lbWordStyle = LineBreakConfig.LINE_BREAK_STYLE_UNSPECIFIED;
+                    if (lbWordStyleStr != null) {
+                        if (lbWordStyleStr.equals("none")) {
+                            lbWordStyle = LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE;
+                        } else if (lbWordStyleStr.equals("phrase")) {
+                            lbWordStyle = LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE;
+                        } else {
+                            Log.w(TAG, "Unknown LineBreakConfig word style: " + lbWordStyleStr);
+                        }
+                    }
+
+                    // Attach span only when the both lbStyle and lbWordStyle are valid.
+                    if (lbStyle != LineBreakConfig.LINE_BREAK_STYLE_UNSPECIFIED
+                            || lbWordStyle != LineBreakConfig.LINE_BREAK_WORD_STYLE_UNSPECIFIED) {
+                        buffer.setSpan(new LineBreakConfigSpan(
+                                new LineBreakConfig(lbStyle, lbWordStyle,
+                                        LineBreakConfig.HYPHENATION_UNSPECIFIED)),
+                                style[i + 1], style[i + 2] + 1,
+                                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+                    }
                 }
             }
 
diff --git a/core/java/android/os/BatteryManager.java b/core/java/android/os/BatteryManager.java
index 6a4ec9b..25fba60 100644
--- a/core/java/android/os/BatteryManager.java
+++ b/core/java/android/os/BatteryManager.java
@@ -21,6 +21,7 @@
 import android.Manifest.permission;
 import android.annotation.FlaggedApi;
 import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
@@ -236,6 +237,7 @@
                                             OsProtoEnums.CHARGING_POLICY_ADAPTIVE_LONGLIFE; // = 4
 
     /** @hide */
+    @SuppressLint("UnflaggedApi") // TestApi without associated feature.
     @TestApi
     public static final int BATTERY_PLUGGED_ANY =
             BATTERY_PLUGGED_AC | BATTERY_PLUGGED_USB | BATTERY_PLUGGED_WIRELESS
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 4e3deb6..c0a5629 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -47,6 +47,7 @@
 import android.text.style.ForegroundColorSpan;
 import android.text.style.LeadingMarginSpan;
 import android.text.style.LineBackgroundSpan;
+import android.text.style.LineBreakConfigSpan;
 import android.text.style.LineHeightSpan;
 import android.text.style.LocaleSpan;
 import android.text.style.ParagraphStyle;
@@ -787,7 +788,9 @@
     /** @hide */
     public static final int ACCESSIBILITY_REPLACEMENT_SPAN = 29;
     /** @hide */
-    public static final int LAST_SPAN = ACCESSIBILITY_REPLACEMENT_SPAN;
+    public static final int LINE_BREAK_CONFIG_SPAN = 30;
+    /** @hide */
+    public static final int LAST_SPAN = LINE_BREAK_CONFIG_SPAN;
 
     /**
      * Flatten a CharSequence and whatever styles can be copied across processes
@@ -991,6 +994,10 @@
                     span = new AccessibilityReplacementSpan(p);
                     break;
 
+                case LINE_BREAK_CONFIG_SPAN:
+                    span = LineBreakConfigSpan.CREATOR.createFromParcel(p);
+                    break;
+
                 default:
                     throw new RuntimeException("bogus span encoding " + kind);
                 }
diff --git a/core/java/android/text/style/LineBreakConfigSpan.java b/core/java/android/text/style/LineBreakConfigSpan.java
index 682ffa1..eeb6383 100644
--- a/core/java/android/text/style/LineBreakConfigSpan.java
+++ b/core/java/android/text/style/LineBreakConfigSpan.java
@@ -21,6 +21,9 @@
 import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.graphics.text.LineBreakConfig;
+import android.os.Parcel;
+import android.text.ParcelableSpan;
+import android.text.TextUtils;
 
 import java.util.Objects;
 
@@ -28,7 +31,7 @@
  * LineBreakSpan for changing line break style of the specific region of the text.
  */
 @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN)
-public class LineBreakConfigSpan {
+public final class LineBreakConfigSpan implements ParcelableSpan {
     private final LineBreakConfig mLineBreakConfig;
 
     /**
@@ -49,6 +52,28 @@
         return mLineBreakConfig;
     }
 
+    /**
+     * A specialized {@link LineBreakConfigSpan} that used for preventing line break.
+     *
+     * This is useful when you want to preserve some words in the same line.
+     * Note that even if this style is specified, the grapheme based line break is still performed
+     * for preventing clipping text.
+     *
+     * @see LineBreakConfigSpan
+     */
+    @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN)
+    public static @NonNull LineBreakConfigSpan createNoBreakSpan() {
+        return new LineBreakConfigSpan(sNoBreakConfig);
+    }
+
+    /**
+     * A specialized {@link LineBreakConfigSpan} that used for preventing hyphenation.
+     */
+    @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN)
+    public static @NonNull LineBreakConfigSpan createNoHyphenationSpan() {
+        return new LineBreakConfigSpan(sNoHyphenationConfig);
+    }
+
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
@@ -75,37 +100,46 @@
             .setLineBreakStyle(LineBreakConfig.LINE_BREAK_STYLE_NO_BREAK)
             .build();
 
-    /**
-     * A specialized {@link LineBreakConfigSpan} that used for preventing hyphenation.
-     */
-    @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN)
-    public static final class NoHyphenationSpan extends LineBreakConfigSpan {
-        /**
-         * Construct a new {@link NoHyphenationSpan}.
-         */
-        @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN)
-        public NoHyphenationSpan() {
-            super(sNoHyphenationConfig);
-        }
+    @Override
+    public int describeContents() {
+        return 0;
     }
 
-    /**
-     * A specialized {@link LineBreakConfigSpan} that used for preventing line break.
-     *
-     * This is useful when you want to preserve some words in the same line.
-     * Note that even if this style is specified, the grapheme based line break is still performed
-     * for preventing clipping text.
-     *
-     * @see LineBreakConfigSpan
-     */
-    @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN)
-    public static final class NoBreakSpan extends LineBreakConfigSpan {
-        /**
-         * Construct a new {@link NoBreakSpan}.
-         */
-        @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN)
-        public NoBreakSpan() {
-            super(sNoBreakConfig);
-        }
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        writeToParcelInternal(dest, flags);
     }
+
+    @Override
+    public int getSpanTypeId() {
+        return getSpanTypeIdInternal();
+    }
+
+    /** @hide */
+    @Override
+    public int getSpanTypeIdInternal() {
+        return TextUtils.LINE_BREAK_CONFIG_SPAN;
+    }
+
+    /** @hide */
+    @Override
+    public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
+        dest.writeParcelable(mLineBreakConfig, flags);
+    }
+
+    @NonNull
+    public static final Creator<LineBreakConfigSpan> CREATOR = new Creator<>() {
+
+        @Override
+        public LineBreakConfigSpan createFromParcel(Parcel source) {
+            LineBreakConfig lbc = source.readParcelable(
+                    LineBreakConfig.class.getClassLoader(), LineBreakConfig.class);
+            return new LineBreakConfigSpan(lbc);
+        }
+
+        @Override
+        public LineBreakConfigSpan[] newArray(int size) {
+            return new LineBreakConfigSpan[size];
+        }
+    };
 }
diff --git a/core/java/android/view/AttachedSurfaceControl.java b/core/java/android/view/AttachedSurfaceControl.java
index 71d382e..d545751 100644
--- a/core/java/android/view/AttachedSurfaceControl.java
+++ b/core/java/android/view/AttachedSurfaceControl.java
@@ -15,14 +15,18 @@
  */
 package android.view;
 
+import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UiThread;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.hardware.HardwareBuffer;
+import android.os.IBinder;
 import android.window.SurfaceSyncGroup;
 
+import com.android.window.flags.Flags;
+
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
 
@@ -178,6 +182,21 @@
     }
 
     /**
+     * Gets the token used for associating this {@link AttachedSurfaceControl} with
+     * {@link SurfaceControlViewHost} instances.
+     *
+     * <p>This token should be passed to {@link SurfaceControlViewHost}'s constructor.
+     *
+     * @return The SurfaceControlViewHost link token.
+     */
+    @Nullable
+    @FlaggedApi(Flags.FLAG_GET_HOST_TOKEN_API)
+    default IBinder getHostToken() {
+        throw new UnsupportedOperationException("The getHostToken needs to be "
+            + "implemented before making this call.");
+    }
+
+    /**
      * Add a trusted presentation listener on the SurfaceControl associated with this window.
      *
      * @param t          Transaction that the trusted presentation listener is added on. This should
diff --git a/core/java/android/view/SurfaceControlRegistry.java b/core/java/android/view/SurfaceControlRegistry.java
index 52be8f6..127d4a7 100644
--- a/core/java/android/view/SurfaceControlRegistry.java
+++ b/core/java/android/view/SurfaceControlRegistry.java
@@ -337,13 +337,13 @@
     @VisibleForTesting
     public final boolean matchesForCallStackDebugging(@Nullable String name, @NonNull String call) {
         final boolean matchCall = !sCallStackDebuggingMatchCall.isEmpty();
-        if (matchCall && !call.toLowerCase().contains(sCallStackDebuggingMatchCall)) {
+        if (matchCall && !sCallStackDebuggingMatchCall.contains(call.toLowerCase())) {
             // Skip if target call doesn't match requested caller
             return false;
         }
         final boolean matchName = !sCallStackDebuggingMatchName.isEmpty();
         if (matchName && (name == null
-                || !name.toLowerCase().contains(sCallStackDebuggingMatchName))) {
+                || !sCallStackDebuggingMatchName.contains(name.toLowerCase()))) {
             // Skip if target surface doesn't match requested surface
             return false;
         }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 4da02f9..2422369 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -93,6 +93,7 @@
 import android.animation.AnimationHandler;
 import android.animation.LayoutTransition;
 import android.annotation.AnyThread;
+import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.Size;
@@ -227,6 +228,7 @@
 import com.android.internal.view.BaseSurfaceHolder;
 import com.android.internal.view.RootViewSurfaceTaker;
 import com.android.internal.view.SurfaceCallbackHelper;
+import com.android.window.flags.Flags;
 
 import java.io.IOException;
 import java.io.OutputStream;
@@ -3962,9 +3964,15 @@
             // on a different thread. However, when the current process is system, the finishDraw in
             // system server will be run on the current thread, which could result in a deadlock.
             if (mWindowSession instanceof Binder) {
-                reportDrawFinished(t, seqId);
+                // The transaction should be copied to a local reference when posting onto a new
+                // thread because up until now the SSG is holding a lock on the transaction. Once
+                // the call jumps onto a new thread, the lock is no longer held and the transaction
+                // send back may be modified or used again.
+                Transaction transactionCopy = new Transaction();
+                transactionCopy.merge(t);
+                mHandler.postAtFrontOfQueue(() -> reportDrawFinished(transactionCopy, seqId));
             } else {
-                mHandler.postAtFrontOfQueue(() -> reportDrawFinished(t, seqId));
+                reportDrawFinished(t, seqId);
             }
         });
         if (DEBUG_BLAST) {
@@ -3975,8 +3983,17 @@
     }
 
     private void notifyContentCaptureEvents() {
-        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "notifyContentCaptureEvents");
         try {
+            if (!isContentCaptureEnabled()) {
+                if (DEBUG_CONTENT_CAPTURE) {
+                    Log.d(mTag, "notifyContentCaptureEvents while disabled");
+                }
+                mAttachInfo.mContentCaptureEvents = null;
+                return;
+            }
+            if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+                Trace.traceBegin(Trace.TRACE_TAG_VIEW, "notifyContentCaptureEvents");
+            }
             MainContentCaptureSession mainSession = mAttachInfo.mContentCaptureManager
                     .getMainContentCaptureSession();
             for (int i = 0; i < mAttachInfo.mContentCaptureEvents.size(); i++) {
@@ -4890,13 +4907,14 @@
         if (DEBUG_CONTENT_CAPTURE) {
             Log.v(mTag, "performContentCaptureInitialReport() on " + rootView);
         }
-        if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
-            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "dispatchContentCapture() for "
-                    + getClass().getSimpleName());
-        }
         try {
             if (!isContentCaptureEnabled()) return;
 
+            if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+                Trace.traceBegin(Trace.TRACE_TAG_VIEW, "dispatchContentCapture() for "
+                        + getClass().getSimpleName());
+            }
+
             // Initial dispatch of window bounds to content capture
             if (mAttachInfo.mContentCaptureManager != null) {
                 MainContentCaptureSession session =
@@ -4916,13 +4934,14 @@
         if (DEBUG_CONTENT_CAPTURE) {
             Log.v(mTag, "handleContentCaptureFlush()");
         }
-        if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
-            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "flushContentCapture for "
-                    + getClass().getSimpleName());
-        }
         try {
             if (!isContentCaptureEnabled()) return;
 
+            if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+                Trace.traceBegin(Trace.TRACE_TAG_VIEW, "flushContentCapture for "
+                        + getClass().getSimpleName());
+            }
+
             final ContentCaptureManager ccm = mAttachInfo.mContentCaptureManager;
             if (ccm == null) {
                 Log.w(TAG, "No ContentCapture on AttachInfo");
@@ -10858,6 +10877,17 @@
         return mInputEventReceiver.getToken();
     }
 
+    /**
+     * @return Returns a token used for associating the root surface
+     * to {@link SurfaceControlViewHost}.
+     */
+    @Nullable
+    @Override
+    @FlaggedApi(Flags.FLAG_GET_HOST_TOKEN_API)
+    public IBinder getHostToken() {
+        return getInputToken();
+    }
+
     @NonNull
     public IBinder getWindowToken() {
         return mAttachInfo.mWindowToken;
diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
index 6888b50..ab9566e 100644
--- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
+++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
@@ -22,3 +22,10 @@
     description: "Enable force force-dark for smart inversion and dark theme everywhere"
     bug: "282821643"
 }
+
+flag {
+    namespace: "accessibility"
+    name: "update_always_on_a11y_service"
+    description: "Updates the Always-On A11yService state when the user changes the enablement of the shortcut."
+    bug: "298869916"
+}
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index 970baf2..5a058ff 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -794,7 +794,9 @@
                 (params.flags & WindowManager.LayoutParams.FLAG_SECURE) != 0;
 
         MainContentCaptureSession mainSession;
+        boolean alreadyDisabledByApp;
         synchronized (mLock) {
+            alreadyDisabledByApp = (mFlags & ContentCaptureContext.FLAG_DISABLED_BY_APP) != 0;
             if (flagSecureEnabled) {
                 mFlags |= ContentCaptureContext.FLAG_DISABLED_BY_FLAG_SECURE;
             } else {
@@ -802,7 +804,9 @@
             }
             mainSession = mMainSession;
         }
-        if (mainSession != null) {
+
+        // Prevent overriding the status of disabling by app
+        if (mainSession != null && !alreadyDisabledByApp) {
             mainSession.setDisabled(flagSecureEnabled);
         }
     }
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index b44d6a4..d9b0f80 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -44,6 +44,7 @@
 import android.os.IBinder;
 import android.os.IBinder.DeathRecipient;
 import android.os.RemoteException;
+import android.os.Trace;
 import android.text.Selection;
 import android.text.Spannable;
 import android.text.TextUtils;
@@ -373,12 +374,26 @@
             return;
         }
 
+        if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+            if (eventType == TYPE_VIEW_TREE_APPEARING) {
+                Trace.asyncTraceBegin(
+                        Trace.TRACE_TAG_VIEW, /* methodName= */ "sendEventAsync", /* cookie= */ 0);
+            }
+        }
+
         if (isContentProtectionReceiverEnabled()) {
             sendContentProtectionEvent(event);
         }
         if (isContentCaptureReceiverEnabled()) {
             sendContentCaptureEvent(event, forceFlush);
         }
+
+        if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+            if (eventType == TYPE_VIEW_TREE_APPEARED) {
+                Trace.asyncTraceEnd(
+                        Trace.TRACE_TAG_VIEW, /* methodName= */ "sendEventAsync", /* cookie= */ 0);
+            }
+        }
     }
 
     @UiThread
diff --git a/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig b/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig
index f6ee061..f3dc33c 100644
--- a/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig
+++ b/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig
@@ -20,3 +20,10 @@
     description: "If true, content protection setting ui is displayed in Settings > Privacy & Security > More security & privacy."
     bug: "305792348"
 }
+
+flag {
+    name: "create_accessibility_overlay_app_op_enabled"
+    namespace: "content_protection"
+    description: "If true, an appop is logged on creation of accessibility overlays."
+    bug: "289081465"
+}
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 3078801..403b403 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -85,6 +85,7 @@
 import android.util.LongArray;
 import android.util.Pair;
 import android.util.SizeF;
+import android.util.SparseArray;
 import android.util.SparseIntArray;
 import android.util.TypedValue;
 import android.util.TypedValue.ComplexDimensionUnit;
@@ -367,6 +368,11 @@
     @UnsupportedAppUsage
     private BitmapCache mBitmapCache = new BitmapCache();
 
+    /**
+     * Maps Intent ID to RemoteCollectionItems to avoid duplicate items
+     */
+    private RemoteCollectionCache mCollectionCache = new RemoteCollectionCache();
+
     /** Cache of ApplicationInfos used by collection items. */
     private ApplicationInfoCache mApplicationInfoCache = new ApplicationInfoCache();
 
@@ -784,9 +790,12 @@
                 if (action instanceof SetRemoteCollectionItemListAdapterAction itemsAction
                         && itemsAction.mViewId == viewId
                         && itemsAction.mServiceIntent != null) {
-                    mActions.set(i,
-                            new SetRemoteCollectionItemListAdapterAction(itemsAction.mViewId,
-                                    itemsAction.mServiceIntent));
+                    SetRemoteCollectionItemListAdapterAction newCollectionAction =
+                            new SetRemoteCollectionItemListAdapterAction(
+                                    itemsAction.mViewId, itemsAction.mServiceIntent);
+                    newCollectionAction.mIntentId = itemsAction.mIntentId;
+                    newCollectionAction.mIsReplacedIntoAction = true;
+                    mActions.set(i, newCollectionAction);
                     isActionReplaced = true;
                 } else if (action instanceof SetRemoteViewsAdapterIntent intentAction
                         && intentAction.mViewId == viewId) {
@@ -1048,6 +1057,8 @@
         @NonNull
         private CompletableFuture<RemoteCollectionItems> mItemsFuture;
         final Intent mServiceIntent;
+        int mIntentId = -1;
+        boolean mIsReplacedIntoAction = false;
 
         SetRemoteCollectionItemListAdapterAction(@IdRes int id,
                 @NonNull RemoteCollectionItems items) {
@@ -1108,38 +1119,36 @@
 
         SetRemoteCollectionItemListAdapterAction(Parcel parcel) {
             mViewId = parcel.readInt();
-            mItemsFuture = CompletableFuture.completedFuture(
-                    new RemoteCollectionItems(parcel, getHierarchyRootData()));
+            mIntentId = parcel.readInt();
+            mItemsFuture = CompletableFuture.completedFuture(mIntentId != -1
+                    ? null
+                    : new RemoteCollectionItems(parcel, getHierarchyRootData()));
             mServiceIntent = parcel.readTypedObject(Intent.CREATOR);
         }
 
         @Override
         public void setHierarchyRootData(HierarchyRootData rootData) {
-            mItemsFuture = mItemsFuture
-                    .thenApply(rc -> {
-                        rc.setHierarchyRootData(rootData);
-                        return rc;
-                    });
-        }
-
-        private static RemoteCollectionItems getCollectionItemsFromFuture(
-                CompletableFuture<RemoteCollectionItems> itemsFuture) {
-            RemoteCollectionItems items;
-            try {
-                items = itemsFuture.get();
-            } catch (Exception e) {
-                Log.e(LOG_TAG, "Error getting collection items from future", e);
-                items = new RemoteCollectionItems.Builder().build();
+            if (mIntentId == -1) {
+                mItemsFuture = mItemsFuture
+                        .thenApply(rc -> {
+                            rc.setHierarchyRootData(rootData);
+                            return rc;
+                        });
+                return;
             }
 
-            return items;
+            // Set the root data for items in the cache instead
+            mCollectionCache.setHierarchyDataForId(mIntentId, rootData);
         }
 
         @Override
         public void writeToParcel(Parcel dest, int flags) {
             dest.writeInt(mViewId);
-            RemoteCollectionItems items = getCollectionItemsFromFuture(mItemsFuture);
-            items.writeToParcel(dest, flags, /* attached= */ true);
+            dest.writeInt(mIntentId);
+            if (mIntentId == -1) {
+                RemoteCollectionItems items = getCollectionItemsFromFuture(mItemsFuture);
+                items.writeToParcel(dest, flags, /* attached= */ true);
+            }
             dest.writeTypedObject(mServiceIntent, flags);
         }
 
@@ -1149,7 +1158,9 @@
             View target = root.findViewById(mViewId);
             if (target == null) return;
 
-            RemoteCollectionItems items = getCollectionItemsFromFuture(mItemsFuture);
+            RemoteCollectionItems items = mIntentId == -1
+                    ? getCollectionItemsFromFuture(mItemsFuture)
+                    : mCollectionCache.getItemsForId(mIntentId);
 
             // Ensure that we are applying to an AppWidget root
             if (!(rootParent instanceof AppWidgetHostView)) {
@@ -1210,6 +1221,153 @@
         }
     }
 
+    private static RemoteCollectionItems getCollectionItemsFromFuture(
+            CompletableFuture<RemoteCollectionItems> itemsFuture) {
+        RemoteCollectionItems items;
+        try {
+            items = itemsFuture.get();
+        } catch (Exception e) {
+            Log.e(LOG_TAG, "Error getting collection items from future", e);
+            items = new RemoteCollectionItems.Builder().build();
+        }
+
+        return items;
+    }
+
+    /**
+     * @hide
+     */
+    public void collectAllIntents() {
+        mCollectionCache.collectAllIntentsNoComplete(this);
+    }
+
+    private class RemoteCollectionCache {
+        private SparseArray<String> mIdToUriMapping = new SparseArray<>();
+        private HashMap<String, RemoteCollectionItems> mUriToCollectionMapping = new HashMap<>();
+
+        // We don't put this into the parcel
+        private HashMap<String, CompletableFuture<RemoteCollectionItems>> mTempUriToFutureMapping =
+                new HashMap<>();
+
+        RemoteCollectionCache() { }
+
+        RemoteCollectionCache(RemoteCollectionCache src) {
+            boolean isWaitingCache = src.mTempUriToFutureMapping.size() != 0;
+            for (int i = 0; i < src.mIdToUriMapping.size(); i++) {
+                String uri = src.mIdToUriMapping.valueAt(i);
+                mIdToUriMapping.put(src.mIdToUriMapping.keyAt(i), uri);
+                if (isWaitingCache) {
+                    mTempUriToFutureMapping.put(uri, src.mTempUriToFutureMapping.get(uri));
+                } else {
+                    mUriToCollectionMapping.put(uri, src.mUriToCollectionMapping.get(uri));
+                }
+            }
+        }
+
+        RemoteCollectionCache(Parcel in) {
+            int cacheSize = in.readInt();
+            HierarchyRootData currentRootData = new HierarchyRootData(mBitmapCache,
+                    this,
+                    mApplicationInfoCache,
+                    mClassCookies);
+            for (int i = 0; i < cacheSize; i++) {
+                int intentId = in.readInt();
+                String intentUri = in.readString8();
+                RemoteCollectionItems items = new RemoteCollectionItems(in, currentRootData);
+                mIdToUriMapping.put(intentId, intentUri);
+                mUriToCollectionMapping.put(intentUri, items);
+            }
+        }
+
+        void setHierarchyDataForId(int intentId, HierarchyRootData data) {
+            String uri = mIdToUriMapping.get(intentId);
+            if (mTempUriToFutureMapping.get(uri) != null) {
+                CompletableFuture<RemoteCollectionItems> itemsFuture =
+                        mTempUriToFutureMapping.get(uri);
+                mTempUriToFutureMapping.put(uri, itemsFuture.thenApply(rc -> {
+                    rc.setHierarchyRootData(data);
+                    return rc;
+                }));
+
+                return;
+            }
+
+            RemoteCollectionItems items = mUriToCollectionMapping.get(uri);
+            items.setHierarchyRootData(data);
+        }
+
+        RemoteCollectionItems getItemsForId(int intentId) {
+            String uri = mIdToUriMapping.get(intentId);
+            return mUriToCollectionMapping.get(uri);
+        }
+
+        void collectAllIntentsNoComplete(@NonNull RemoteViews inViews) {
+            if (inViews.hasSizedRemoteViews()) {
+                for (RemoteViews remoteViews : inViews.mSizedRemoteViews) {
+                    remoteViews.collectAllIntents();
+                }
+            } else if (inViews.hasLandscapeAndPortraitLayouts()) {
+                inViews.mLandscape.collectAllIntents();
+                inViews.mPortrait.collectAllIntents();
+            } else if (inViews.mActions != null) {
+                for (Action action : inViews.mActions) {
+                    if (action instanceof SetRemoteCollectionItemListAdapterAction rca) {
+                        // Deal with the case where the intent is replaced into the action list
+                        if (rca.mIntentId != -1 && !rca.mIsReplacedIntoAction) {
+                            continue;
+                        }
+
+                        if (rca.mIntentId != -1 && rca.mIsReplacedIntoAction) {
+                            String uri = mIdToUriMapping.get(rca.mIntentId);
+                            mTempUriToFutureMapping.put(uri, rca.mItemsFuture);
+                            rca.mItemsFuture = CompletableFuture.completedFuture(null);
+                            continue;
+                        }
+
+                        // Differentiate between the normal collection actions and the ones with
+                        // intents.
+                        if (rca.mServiceIntent != null) {
+                            String uri = rca.mServiceIntent.toUri(0);
+                            int index = mIdToUriMapping.indexOfValue(uri);
+                            if (index == -1) {
+                                int newIntentId = mIdToUriMapping.size();
+                                rca.mIntentId = newIntentId;
+                                mIdToUriMapping.put(newIntentId, uri);
+                                // mUriToIntentMapping.put(uri, mServiceIntent);
+                                mTempUriToFutureMapping.put(uri, rca.mItemsFuture);
+                            } else {
+                                rca.mIntentId = mIdToUriMapping.keyAt(index);
+                            }
+                            rca.mItemsFuture = CompletableFuture.completedFuture(null);
+                        } else {
+                            RemoteCollectionItems items = getCollectionItemsFromFuture(
+                                    rca.mItemsFuture);
+                            for (RemoteViews views : items.mViews) {
+                                views.collectAllIntents();
+                            }
+                        }
+                    } else if (action instanceof ViewGroupActionAdd vgaa
+                            && vgaa.mNestedViews != null) {
+                        vgaa.mNestedViews.collectAllIntents();
+                    }
+                }
+            }
+        }
+
+        public void writeToParcel(Parcel out, int flags) {
+            out.writeInt(mIdToUriMapping.size());
+            for (int i = 0; i < mIdToUriMapping.size(); i++) {
+                out.writeInt(mIdToUriMapping.keyAt(i));
+                String intentUri = mIdToUriMapping.valueAt(i);
+                out.writeString8(intentUri);
+                RemoteCollectionItems items = mTempUriToFutureMapping.get(intentUri) != null
+                        ? getCollectionItemsFromFuture(mTempUriToFutureMapping.get(intentUri))
+                        : mUriToCollectionMapping.get(intentUri);
+                items.writeToParcel(out, flags, true);
+            }
+        }
+    }
+
     private class SetRemoteViewsAdapterIntent extends Action {
         Intent mIntent;
         boolean mIsAsync = false;
@@ -3850,9 +4008,12 @@
     private void initializeFrom(@NonNull RemoteViews src, @Nullable RemoteViews hierarchyRoot) {
         if (hierarchyRoot == null) {
             mBitmapCache = src.mBitmapCache;
+            // We need to create a new instance because we don't reconstruct collection cache
+            mCollectionCache = new RemoteCollectionCache(src.mCollectionCache);
             mApplicationInfoCache = src.mApplicationInfoCache;
         } else {
             mBitmapCache = hierarchyRoot.mBitmapCache;
+            mCollectionCache = hierarchyRoot.mCollectionCache;
             mApplicationInfoCache = hierarchyRoot.mApplicationInfoCache;
         }
         if (hierarchyRoot == null || src.mIsRoot) {
@@ -3926,6 +4087,7 @@
             mBitmapCache = new BitmapCache(parcel);
             // Store the class cookies such that they are available when we clone this RemoteView.
             mClassCookies = parcel.copyClassCookies();
+            mCollectionCache = new RemoteCollectionCache(parcel);
         } else {
             configureAsChild(rootData);
         }
@@ -4087,6 +4249,7 @@
     private void configureAsChild(@NonNull HierarchyRootData rootData) {
         mIsRoot = false;
         mBitmapCache = rootData.mBitmapCache;
+        mCollectionCache = rootData.mRemoteCollectionCache;
         mApplicationInfoCache = rootData.mApplicationInfoCache;
         mClassCookies = rootData.mClassCookies;
         configureDescendantsAsChildren();
@@ -6357,6 +6520,7 @@
             // is shared by all children.
             if (mIsRoot) {
                 mBitmapCache.writeBitmapsToParcel(dest, flags);
+                mCollectionCache.writeToParcel(dest, flags);
             }
             mApplication.writeToParcel(dest, flags);
             if (mIsRoot || mIdealSize == null) {
@@ -6373,6 +6537,7 @@
             dest.writeInt(MODE_HAS_SIZED_REMOTEVIEWS);
             if (mIsRoot) {
                 mBitmapCache.writeBitmapsToParcel(dest, flags);
+                mCollectionCache.writeToParcel(dest, flags);
             }
             dest.writeInt(mSizedRemoteViews.size());
             for (RemoteViews view : mSizedRemoteViews) {
@@ -6384,6 +6549,7 @@
             // is shared by all children.
             if (mIsRoot) {
                 mBitmapCache.writeBitmapsToParcel(dest, flags);
+                mCollectionCache.writeToParcel(dest, flags);
             }
             mLandscape.writeToParcel(dest, flags);
             // Both RemoteViews already share the same package and user
@@ -7262,19 +7428,23 @@
     }
 
     private HierarchyRootData getHierarchyRootData() {
-        return new HierarchyRootData(mBitmapCache, mApplicationInfoCache, mClassCookies);
+        return new HierarchyRootData(mBitmapCache, mCollectionCache,
+                mApplicationInfoCache, mClassCookies);
     }
 
     private static final class HierarchyRootData {
         final BitmapCache mBitmapCache;
+        final RemoteCollectionCache mRemoteCollectionCache;
         final ApplicationInfoCache mApplicationInfoCache;
         final Map<Class, Object> mClassCookies;
 
         HierarchyRootData(
                 BitmapCache bitmapCache,
+                RemoteCollectionCache remoteCollectionCache,
                 ApplicationInfoCache applicationInfoCache,
                 Map<Class, Object> classCookies) {
             mBitmapCache = bitmapCache;
+            mRemoteCollectionCache = remoteCollectionCache;
             mApplicationInfoCache = applicationInfoCache;
             mClassCookies = classCookies;
         }
diff --git a/core/java/android/window/WindowTokenClient.java b/core/java/android/window/WindowTokenClient.java
index f4f1b3b..c20b278 100644
--- a/core/java/android/window/WindowTokenClient.java
+++ b/core/java/android/window/WindowTokenClient.java
@@ -31,10 +31,8 @@
 import android.inputmethodservice.AbstractInputMethodService;
 import android.os.Binder;
 import android.os.Build;
-import android.os.Bundle;
 import android.os.Debug;
 import android.os.Handler;
-import android.os.IBinder;
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
@@ -76,8 +74,7 @@
      * Attaches {@code context} to this {@link WindowTokenClient}. Each {@link WindowTokenClient}
      * can only attach one {@link Context}.
      * <p>This method must be called before invoking
-     * {@link android.view.IWindowManager#attachWindowContextToDisplayArea(IBinder, int, int,
-     * Bundle)}.<p/>
+     * {@link android.view.IWindowManager#attachWindowContextToDisplayArea}.<p/>
      *
      * @param context context to be attached
      * @throws IllegalStateException if attached context has already existed.
diff --git a/core/java/android/window/flags/responsible_apis.aconfig b/core/java/android/window/flags/responsible_apis.aconfig
new file mode 100644
index 0000000..4bfb177
--- /dev/null
+++ b/core/java/android/window/flags/responsible_apis.aconfig
@@ -0,0 +1,22 @@
+package: "com.android.window.flags"
+
+flag {
+    name: "bal_require_opt_in_by_pending_intent_creator"
+    namespace: "responsible_apis"
+    description: "Require the PendingIntent creator to opt in starting with Android 15"
+    bug: "296478951"
+}
+
+flag {
+    name: "bal_dont_bring_existing_background_task_stack_to_fg"
+    namespace: "responsible_apis"
+    description: "When starting a PendingIntent with ONLY creator privileges, don't bring the existing task stack to foreground"
+    bug: "296478675"
+}
+
+flag {
+    name: "bal_show_toasts"
+    namespace: "responsible_apis"
+    description: "Enable toasts to indicate (potential) BAL blocking."
+    bug: "308059069"
+}
\ No newline at end of file
diff --git a/core/java/android/window/flags/wallpaper_manager.aconfig b/core/java/android/window/flags/wallpaper_manager.aconfig
new file mode 100644
index 0000000..09be0cf
--- /dev/null
+++ b/core/java/android/window/flags/wallpaper_manager.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.window.flags"
+
+flag {
+    name: "always_update_wallpaper_permission"
+    namespace: "wear_frameworks"
+    description: "Allow out of focus process to update wallpaper complications"
+    bug: "271132915"
+}
\ No newline at end of file
diff --git a/core/java/android/window/flags/window_surfaces.aconfig b/core/java/android/window/flags/window_surfaces.aconfig
index ccbf4a9..5ad5c79 100644
--- a/core/java/android/window/flags/window_surfaces.aconfig
+++ b/core/java/android/window/flags/window_surfaces.aconfig
@@ -17,3 +17,11 @@
     is_fixed_read_only: true
     bug: "300019131"
 }
+
+flag {
+    namespace: "window_surfaces"
+    name: "get_host_token_api"
+    description: "Feature flag to associate the host and embedded windows"
+    is_fixed_read_only: true
+    bug: "304508760"
+}
diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index 2efe445..de0f070 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -53,10 +53,13 @@
 import android.view.Window;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.Flags;
 import android.widget.Toast;
 
 import com.android.internal.R;
+import com.android.internal.accessibility.common.ShortcutConstants;
 import com.android.internal.accessibility.dialog.AccessibilityTarget;
+import com.android.internal.accessibility.util.ShortcutUtils;
 import com.android.internal.util.function.pooled.PooledLambda;
 
 import java.lang.annotation.Retention;
@@ -66,6 +69,7 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * Class to help manage the accessibility shortcut key
@@ -364,9 +368,23 @@
                         })
                 .setPositiveButton(R.string.accessibility_shortcut_off,
                         (DialogInterface d, int which) -> {
-                            Settings.Secure.putStringForUser(mContext.getContentResolver(),
-                                    Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, "",
-                                    userId);
+                            if (Flags.updateAlwaysOnA11yService()) {
+                                Set<String> targetServices =
+                                        ShortcutUtils.getShortcutTargetsFromSettings(
+                                                mContext,
+                                                ShortcutConstants.UserShortcutType.HARDWARE,
+                                                userId);
+
+                                Settings.Secure.putStringForUser(mContext.getContentResolver(),
+                                        Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, "",
+                                        userId);
+                                ShortcutUtils.updateInvisibleToggleAccessibilityServiceEnableState(
+                                        mContext, targetServices, userId);
+                            } else {
+                                Settings.Secure.putStringForUser(mContext.getContentResolver(),
+                                        Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, "",
+                                        userId);
+                            }
 
                             // If canceled, treat as if the dialog has never been shown
                             Settings.Secure.putIntForUser(mContext.getContentResolver(),
diff --git a/core/java/com/android/internal/accessibility/common/ShortcutConstants.java b/core/java/com/android/internal/accessibility/common/ShortcutConstants.java
index e636004..7ec8838 100644
--- a/core/java/com/android/internal/accessibility/common/ShortcutConstants.java
+++ b/core/java/com/android/internal/accessibility/common/ShortcutConstants.java
@@ -60,6 +60,17 @@
     }
 
     /**
+     * A list of possible {@link UserShortcutType}. Should stay in sync with the
+     * non-default IntDef types.
+     */
+    public static final int[] USER_SHORTCUT_TYPES = {
+            UserShortcutType.SOFTWARE,
+            UserShortcutType.HARDWARE,
+            UserShortcutType.TRIPLETAP
+    };
+
+
+    /**
      * Annotation for the different accessibility fragment type.
      *
      * {@code VOLUME_SHORTCUT_TOGGLE} for displaying appearance with switch bar and only one
diff --git a/core/java/com/android/internal/accessibility/dialog/InvisibleToggleAccessibilityServiceTarget.java b/core/java/com/android/internal/accessibility/dialog/InvisibleToggleAccessibilityServiceTarget.java
index 9d5c374..1bc8b84 100644
--- a/core/java/com/android/internal/accessibility/dialog/InvisibleToggleAccessibilityServiceTarget.java
+++ b/core/java/com/android/internal/accessibility/dialog/InvisibleToggleAccessibilityServiceTarget.java
@@ -27,17 +27,25 @@
 import android.annotation.NonNull;
 import android.content.ComponentName;
 import android.content.Context;
+import android.os.UserHandle;
 import android.view.accessibility.AccessibilityManager.ShortcutType;
+import android.view.accessibility.Flags;
 
 import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType;
+import com.android.internal.accessibility.util.ShortcutUtils;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Set;
 
 /**
  * Extension for {@link AccessibilityServiceTarget} with
  * {@link AccessibilityFragmentType#INVISIBLE_TOGGLE} type.
  */
-class InvisibleToggleAccessibilityServiceTarget extends AccessibilityServiceTarget {
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public class InvisibleToggleAccessibilityServiceTarget extends AccessibilityServiceTarget {
 
-    InvisibleToggleAccessibilityServiceTarget(Context context, @ShortcutType int shortcutType,
+    public InvisibleToggleAccessibilityServiceTarget(
+            Context context, @ShortcutType int shortcutType,
             @NonNull AccessibilityServiceInfo serviceInfo) {
         super(context,
                 shortcutType,
@@ -49,11 +57,17 @@
     public void onCheckedChanged(boolean isChecked) {
         final ComponentName componentName = ComponentName.unflattenFromString(getId());
 
-        if (!isComponentIdExistingInOtherShortcut()) {
-            setAccessibilityServiceState(getContext(), componentName, isChecked);
-        }
+        if (Flags.updateAlwaysOnA11yService()) {
+            super.onCheckedChanged(isChecked);
+            ShortcutUtils.updateInvisibleToggleAccessibilityServiceEnableState(
+                    getContext(), Set.of(componentName.flattenToString()), UserHandle.myUserId());
+        } else {
+            if (!isComponentIdExistingInOtherShortcut()) {
+                setAccessibilityServiceState(getContext(), componentName, isChecked);
+            }
 
-        super.onCheckedChanged(isChecked);
+            super.onCheckedChanged(isChecked);
+        }
     }
 
     private boolean isComponentIdExistingInOtherShortcut() {
diff --git a/core/java/com/android/internal/accessibility/util/ShortcutUtils.java b/core/java/com/android/internal/accessibility/util/ShortcutUtils.java
index 31ccb6c3..3fd3030 100644
--- a/core/java/com/android/internal/accessibility/util/ShortcutUtils.java
+++ b/core/java/com/android/internal/accessibility/util/ShortcutUtils.java
@@ -15,20 +15,29 @@
  */
 
 package com.android.internal.accessibility.util;
+
 import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON;
 import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY;
 
+import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
+import static com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType.INVISIBLE_TOGGLE;
 import static com.android.internal.accessibility.common.ShortcutConstants.SERVICES_SEPARATOR;
+import static com.android.internal.accessibility.common.ShortcutConstants.USER_SHORTCUT_TYPES;
 import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType;
 
+import android.accessibilityservice.AccessibilityServiceInfo;
 import android.annotation.NonNull;
+import android.content.ComponentName;
 import android.content.Context;
 import android.provider.Settings;
 import android.text.TextUtils;
+import android.util.ArraySet;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityManager.ShortcutType;
 
+import java.util.Collections;
 import java.util.List;
+import java.util.Set;
 import java.util.StringJoiner;
 
 /**
@@ -180,4 +189,92 @@
                         "Unsupported shortcut type:" + type);
         }
     }
+
+    /**
+     * Updates an accessibility state if the accessibility service is a Always-On a11y service,
+     * a.k.a. AccessibilityServices that has FLAG_REQUEST_ACCESSIBILITY_BUTTON
+     * <p>
+     * Turn on the accessibility service when there is any shortcut associated to it.
+     * <p>
+     * Turn off the accessibility service when there is no shortcut associated to it.
+     *
+     * @param componentNames the a11y shortcut target's component names
+     */
+    public static void updateInvisibleToggleAccessibilityServiceEnableState(
+            Context context, Set<String> componentNames, int userId) {
+        final AccessibilityManager am = (AccessibilityManager) context.getSystemService(
+                Context.ACCESSIBILITY_SERVICE);
+        if (am == null) return;
+
+        final List<AccessibilityServiceInfo> installedServices =
+                am.getInstalledAccessibilityServiceList();
+
+        final Set<String> invisibleToggleServices = new ArraySet<>();
+        for (AccessibilityServiceInfo serviceInfo : installedServices) {
+            if (AccessibilityUtils.getAccessibilityServiceFragmentType(serviceInfo)
+                    == INVISIBLE_TOGGLE) {
+                invisibleToggleServices.add(serviceInfo.getComponentName().flattenToString());
+            }
+        }
+
+        final Set<String> servicesWithShortcuts = new ArraySet<>();
+        for (int shortcutType: USER_SHORTCUT_TYPES) {
+            // The call to update always-on service might modify the shortcut setting right before
+            // calling #updateAccessibilityServiceStateIfNeeded in the same call.
+            // To avoid getting the shortcut target from out-dated value, use values from Settings
+            // instead.
+            servicesWithShortcuts.addAll(
+                    getShortcutTargetsFromSettings(context, shortcutType, userId));
+        }
+
+        for (String componentName : componentNames) {
+            // Only needs to update the Always-On A11yService's state when the shortcut changes.
+            if (invisibleToggleServices.contains(componentName)) {
+
+                boolean enableA11yService = servicesWithShortcuts.contains(componentName);
+                AccessibilityUtils.setAccessibilityServiceState(
+                        context,
+                        ComponentName.unflattenFromString(componentName), enableA11yService);
+            }
+        }
+    }
+
+    /**
+     * Returns the target component names of a given user shortcut type from Settings.
+     *
+     * <p>
+     * Note: grab shortcut targets from Settings is only needed
+     * if you depends on a value being set in the same call.
+     * For example, you disable a single shortcut,
+     * and you're checking if there is any shortcut remaining.
+     *
+     * <p>
+     * If you just want to know the current state, you can use
+     * {@link AccessibilityManager#getAccessibilityShortcutTargets(int)}
+     */
+    public static Set<String> getShortcutTargetsFromSettings(
+            Context context, @UserShortcutType int shortcutType, int userId) {
+        final String targetKey = convertToKey(shortcutType);
+        if (Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED.equals(targetKey)) {
+            boolean magnificationEnabled = Settings.Secure.getIntForUser(
+                    context.getContentResolver(), targetKey, /* def= */ 0, userId) == 1;
+            return magnificationEnabled ? Set.of(MAGNIFICATION_CONTROLLER_NAME)
+                    : Collections.emptySet();
+
+        } else {
+            final String targetString = Settings.Secure.getStringForUser(
+                    context.getContentResolver(), targetKey, userId);
+
+            if (TextUtils.isEmpty(targetString)) {
+                return Collections.emptySet();
+            }
+
+            Set<String> targets = new ArraySet<>();
+            sStringColonSplitter.setString(targetString);
+            while (sStringColonSplitter.hasNext()) {
+                targets.add(sStringColonSplitter.next());
+            }
+            return Collections.unmodifiableSet(targets);
+        }
+    }
 }
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index 492e2ac..1b05982 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -42,18 +42,22 @@
 // frameworks/native/libs/permission/include/binder/IAppOpsService.h must match the order here.
 // Please be careful to respect both these issues when modifying this file.
 interface IAppOpsService {
-    // These methods are also called by native code, so please be careful that the number in
-    // frameworks/native/libs/permission/include/binder/IAppOpsService.h matches the ordering here.
+    // Deprecated, use checkOperationWithState instead.
     int checkOperation(int code, int uid, String packageName);
+    // Deprecated, use noteOperationWithState instead.
     SyncNotedAppOp noteOperation(int code, int uid, String packageName, @nullable String attributionTag,
             boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage);
+    // Deprecated, use startOperationWithState instead.
     SyncNotedAppOp startOperation(IBinder clientId, int code, int uid, String packageName,
             @nullable String attributionTag, boolean startIfModeDefault,
             boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
             int attributionFlags, int attributionChainId);
+    // Deprecated, use finishOperationWithState instead.
     @UnsupportedAppUsage
     void finishOperation(IBinder clientId, int code, int uid, String packageName,
             @nullable String attributionTag);
+    // These methods are also called by native code, so please be careful that the number in
+    // frameworks/native/libs/permission/include/binder/IAppOpsService.h matches the ordering here.
     void startWatchingMode(int op, String packageName, IAppOpsCallback callback);
     void stopWatchingMode(IAppOpsCallback callback);
     int permissionToOpCode(String permission);
@@ -134,20 +138,33 @@
     void stopWatchingAsyncNoted(String packageName, IAppOpsAsyncNotedCallback callback);
     List<AsyncNotedAppOp> extractAsyncOps(String packageName);
 
+    // Deprecated, use checkOperationWithStateRaw instead.
     int checkOperationRaw(int code, int uid, String packageName, @nullable String attributionTag);
     void reloadNonHistoricalState();
 
     void collectNoteOpCallsForValidation(String stackTrace, int op, String packageName, long version);
-
+    // These methods are also called by native code, so please be careful that the number in
+    // frameworks/native/libs/permission/include/binder/IAppOpsService.h matches the ordering here.
+    int checkOperationWithState(int code, in AttributionSourceState attributionSourceState);
+    int checkOperationWithStateRaw(int code, in AttributionSourceState attributionSourceState);
+    SyncNotedAppOp noteOperationWithState(int code, in AttributionSourceState attributionSourceState,
+            boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage);
+    SyncNotedAppOp startOperationWithState(IBinder clientId, int code,
+            in AttributionSourceState attributionSourceState, boolean startIfModeDefault,
+            boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
+            int attributionFlags, int attributionChainId);
+    void finishOperationWithState(IBinder clientId, int code, in AttributionSourceState attributionSourceState);
+    // End of methods also called by native code (there may be more blocks like this of native
+    // methods later in this file).
     SyncNotedAppOp noteProxyOperationWithState(int code,
-                in AttributionSourceState attributionSourceStateState,
-                boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
-                boolean skipProxyOperation);
+            in AttributionSourceState attributionSourceStateState,
+            boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
+            boolean skipProxyOperation);
     SyncNotedAppOp startProxyOperationWithState(IBinder clientId, int code,
-                in AttributionSourceState attributionSourceStateState, boolean startIfModeDefault,
-                boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
-                boolean skipProxyOperation, int proxyAttributionFlags, int proxiedAttributionFlags,
-                int attributionChainId);
+            in AttributionSourceState attributionSourceStateState, boolean startIfModeDefault,
+            boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
+            boolean skipProxyOperation, int proxyAttributionFlags, int proxiedAttributionFlags,
+            int attributionChainId);
     void finishProxyOperationWithState(IBinder clientId, int code,
-                in AttributionSourceState attributionSourceStateState, boolean skipProxyOperation);
+            in AttributionSourceState attributionSourceStateState, boolean skipProxyOperation);
 }
diff --git a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
index 77e1502..df6c153 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
@@ -106,6 +106,10 @@
         /** b/301242692: Visit extra URIs used in notifications to prevent security issues. */
         public static final Flag VISIT_RISKY_URIS = devFlag(
                 "persist.sysui.notification.visit_risky_uris");
+
+        /** b/303716154: For debugging only: use short bitmap duration. */
+        public static final Flag DEBUG_SHORT_BITMAP_DURATION = devFlag(
+                "persist.sysui.notification.debug_short_bitmap_duration");
     }
 
     //// == End of flags.  Everything below this line is the implementation. == ////
diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java
index 0b69030..9d88a23 100644
--- a/core/java/com/android/internal/content/PackageMonitor.java
+++ b/core/java/com/android/internal/content/PackageMonitor.java
@@ -401,6 +401,12 @@
                 if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
                     mChangeType = PACKAGE_UPDATING;
                     onPackageUpdateStarted(pkg, uid);
+                    if (intent.getBooleanExtra(Intent.EXTRA_ARCHIVAL, false)) {
+                        // In case it is a removal event due to archiving, we trigger package
+                        // update event to refresh details like icons, title etc. corresponding to
+                        // the archived app.
+                        onPackageModified(pkg);
+                    }
                 } else {
                     mChangeType = PACKAGE_PERMANENT_CHANGE;
                     // We only consider something to have changed if this is
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 8c91be8..c75f996 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4065,6 +4065,7 @@
     <!-- Allow apps to always update wallpaper by sending data.
         @SystemApi
         @hide
+        @FlaggedApi("com.android.window.flags.always_update_wallpaper_permission")
     -->
     <permission android:name="android.permission.ALWAYS_UPDATE_WALLPAPER"
         android:protectionLevel="internal|role" />
diff --git a/core/res/res/drawable/archived_app_cloud_overlay.xml b/core/res/res/drawable/archived_app_cloud_overlay.xml
new file mode 100644
index 0000000..611e0f3
--- /dev/null
+++ b/core/res/res/drawable/archived_app_cloud_overlay.xml
@@ -0,0 +1,15 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="20dp"
+    android:height="20dp"
+    android:viewportWidth="60"
+    android:viewportHeight="60">
+    <group
+        android:scaleX="1.2"
+        android:scaleY="1.2"
+        android:translateX="15"
+        android:translateY="14">
+        <path
+            android:fillColor="@android:color/white"
+            android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96zM19,18L6,18c-2.21,0 -4,-1.79 -4,-4 0,-2.05 1.53,-3.76 3.56,-3.97l1.07,-0.11 0.5,-0.95C8.08,7.14 9.94,6 12,6c2.62,0 4.88,1.86 5.39,4.43l0.3,1.5 1.53,0.11c1.56,0.1 2.78,1.41 2.78,2.96 0,1.65 -1.35,3 -3,3zM13.45,10h-2.9v3L8,13l4,4 4,-4h-2.55z"/>
+    </group>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 14bbb96..1aa1fea 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1405,6 +1405,7 @@
   <java-symbol type="drawable" name="ic_test_badge_no_background" />
   <java-symbol type="drawable" name="ic_test_icon_badge_experiment" />
   <java-symbol type="drawable" name="ic_instant_icon_badge_bolt" />
+  <java-symbol type="drawable" name="archived_app_cloud_overlay" />
   <java-symbol type="drawable" name="emulator_circular_window_overlay" />
   <java-symbol type="drawable" name="ic_qs_battery_saver" />
   <java-symbol type="drawable" name="ic_qs_bluetooth" />
diff --git a/core/tests/coretests/src/android/app/OWNERS b/core/tests/coretests/src/android/app/OWNERS
index 64f5e6c..5636f9b 100644
--- a/core/tests/coretests/src/android/app/OWNERS
+++ b/core/tests/coretests/src/android/app/OWNERS
@@ -10,3 +10,7 @@
 
 # KeyguardManagerTest
 per-file KeyguardManagerTest.java = file:/services/core/java/com/android/server/locksettings/OWNERS
+
+# Files related to background activity launches
+per-file Background*Start* = file:/BAL_OWNERS
+
diff --git a/core/tests/coretests/src/android/os/PowerManagerTest.java b/core/tests/coretests/src/android/os/PowerManagerTest.java
index 21d1dbb..5d213ca 100644
--- a/core/tests/coretests/src/android/os/PowerManagerTest.java
+++ b/core/tests/coretests/src/android/os/PowerManagerTest.java
@@ -19,11 +19,17 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.os.Flags;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.test.AndroidTestCase;
 
 import androidx.test.InstrumentationRegistry;
@@ -31,6 +37,7 @@
 import androidx.test.uiautomator.UiDevice;
 
 import org.junit.After;
+import org.junit.Rule;
 import org.junit.Test;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
@@ -64,6 +71,10 @@
         System.loadLibrary("powermanagertest_jni");
     }
 
+    // Required for RequiresFlagsEnabled and RequiresFlagsDisabled annotations to take effect.
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
     /**
      * Setup any common data for the upcoming tests.
      */
@@ -454,4 +465,27 @@
         parcelBatterySaverPolicyConfigToNativeAndVerify(bs2);
     }
 
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_BATTERY_SAVER_SUPPORTED_CHECK_API)
+    public void testBatterySaverSupported_isSupported() throws RemoteException {
+        IPowerManager powerManager = mock(IPowerManager.class);
+        PowerManager pm = new PowerManager(mContext, powerManager,
+                mock(IThermalService.class),
+                Handler.createAsync(Looper.getMainLooper()));
+        when(powerManager.isBatterySaverSupported()).thenReturn(true);
+
+        assertTrue(pm.isBatterySaverSupported());
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_BATTERY_SAVER_SUPPORTED_CHECK_API)
+    public void testBatterySaverSupported_isNotSupported() throws RemoteException {
+        IPowerManager powerManager = mock(IPowerManager.class);
+        PowerManager pm = new PowerManager(mContext, powerManager,
+                mock(IThermalService.class),
+                Handler.createAsync(Looper.getMainLooper()));
+        when(powerManager.isBatterySaverSupported()).thenReturn(false);
+
+        assertFalse(pm.isBatterySaverSupported());
+    }
 }
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index 6a9fc04..40fd34e 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -48,6 +48,9 @@
 import android.os.Binder;
 import android.os.SystemProperties;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.provider.Settings;
 import android.util.Log;
@@ -60,6 +63,7 @@
 import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.compatibility.common.util.ShellIdentityUtils;
+import com.android.window.flags.Flags;
 
 import org.junit.After;
 import org.junit.AfterClass;
@@ -97,6 +101,9 @@
     // state after the test completes.
     private static boolean sOriginalTouchMode;
 
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
     @BeforeClass
     public static void setUpClass() {
         sContext = sInstrumentation.getTargetContext();
@@ -338,6 +345,19 @@
         assertThat(view.hasWindowFocus()).isFalse();
     }
 
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_GET_HOST_TOKEN_API)
+    public void whenViewIsAttachedToWindow_getHostToken() {
+        View view = new View(sContext);
+        attachViewToWindow(view);
+
+        mViewRootImpl = view.getViewRootImpl();
+
+        assertThat(mViewRootImpl.getHostToken()).isNotEqualTo(null);
+        assertThat(mViewRootImpl.getHostToken())
+                .isEqualTo(mViewRootImpl.getInputToken());
+    }
+
     /**
      * When window doesn't have focus, keys should be dropped.
      */
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java
index 5c411d5..35ddfdb 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java
@@ -23,6 +23,7 @@
 
 import android.content.ContentCaptureOptions;
 import android.content.Context;
+import android.view.WindowManager;
 
 import com.android.internal.util.RingBuffer;
 
@@ -147,6 +148,52 @@
         assertThat(manager.getFlushViewTreeAppearingEventDisabled()).isFalse();
     }
 
+    @Test
+    public void testUpdateWindowAttribute_setFlagSecure() {
+        final ContentCaptureManager manager =
+                new ContentCaptureManager(mMockContext, mMockContentCaptureManager, EMPTY_OPTIONS);
+        // Ensure main session is created.
+        final MainContentCaptureSession unused = manager.getMainContentCaptureSession();
+        final WindowManager.LayoutParams initialParam = new WindowManager.LayoutParams();
+        initialParam.flags |= WindowManager.LayoutParams.FLAG_SECURE;
+
+        manager.updateWindowAttributes(initialParam);
+
+        assertThat(manager.isContentCaptureEnabled()).isFalse();
+    }
+
+    @Test
+    public void testUpdateWindowAttribute_clearFlagSecure() {
+        final ContentCaptureManager manager =
+                new ContentCaptureManager(mMockContext, mMockContentCaptureManager, EMPTY_OPTIONS);
+        // Ensure main session is created.
+        final MainContentCaptureSession unused = manager.getMainContentCaptureSession();
+        final WindowManager.LayoutParams initialParam = new WindowManager.LayoutParams();
+        initialParam.flags |= WindowManager.LayoutParams.FLAG_SECURE;
+        // Default param does not have FLAG_SECURE set.
+        final WindowManager.LayoutParams resetParam = new WindowManager.LayoutParams();
+
+        manager.updateWindowAttributes(initialParam);
+        manager.updateWindowAttributes(resetParam);
+
+        assertThat(manager.isContentCaptureEnabled()).isTrue();
+    }
+
+    @Test
+    public void testUpdateWindowAttribute_clearFlagSecureAfterDisabledByApp() {
+        final ContentCaptureManager manager =
+                new ContentCaptureManager(mMockContext, mMockContentCaptureManager, EMPTY_OPTIONS);
+        // Ensure main session is created.
+        final MainContentCaptureSession unused = manager.getMainContentCaptureSession();
+        // Default param does not have FLAG_SECURE set.
+        final WindowManager.LayoutParams resetParam = new WindowManager.LayoutParams();
+
+        manager.setContentCaptureEnabled(false);
+        manager.updateWindowAttributes(resetParam);
+
+        assertThat(manager.isContentCaptureEnabled()).isFalse();
+    }
+
     private ContentCaptureOptions createOptions(
             ContentCaptureOptions.ContentProtectionOptions contentProtectionOptions) {
         return new ContentCaptureOptions(
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
index cd5ec85..75b0d4a 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
@@ -16,9 +16,11 @@
 
 package com.android.internal.accessibility;
 
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS;
 import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN;
 import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN;
 import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE;
+import static android.provider.Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES;
 import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY;
 
 import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME;
@@ -62,6 +64,9 @@
 import android.os.Handler;
 import android.os.Message;
 import android.os.Vibrator;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.provider.Settings;
 import android.speech.tts.TextToSpeech;
 import android.speech.tts.Voice;
@@ -69,6 +74,7 @@
 import android.view.Window;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.Flags;
 import android.view.accessibility.IAccessibilityManager;
 import android.widget.Toast;
 
@@ -81,6 +87,7 @@
 
 import org.junit.AfterClass;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -96,6 +103,9 @@
 
 @RunWith(AndroidJUnit4.class)
 public class AccessibilityShortcutControllerTest {
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
     private static final String SERVICE_NAME_STRING = "fake.package/fake.service.name";
     private static final CharSequence PACKAGE_NAME_STRING = "Service name";
     private static final String SERVICE_NAME_SUMMARY = "Summary";
@@ -430,6 +440,43 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(Flags.FLAG_UPDATE_ALWAYS_ON_A11Y_SERVICE)
+    public void turnOffVolumeShortcutForAlwaysOnA11yService_shouldTurnOffA11yService()
+            throws Exception {
+        configureApplicationTargetSdkVersion(Build.VERSION_CODES.R);
+        turnOffVolumeKeyShortcutForA11yService(/* alwaysOnService= */ true);
+
+        assertThat(
+                Settings.Secure.getString(mContentResolver, ENABLED_ACCESSIBILITY_SERVICES)
+        ).isEmpty();
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_UPDATE_ALWAYS_ON_A11Y_SERVICE)
+    public void turnOffVolumeShortcutForAlwaysOnA11yService_hasOtherTypesShortcut_shouldNotTurnOffA11yService()
+            throws Exception {
+        configureApplicationTargetSdkVersion(Build.VERSION_CODES.R);
+        Settings.Secure.putString(
+                mContentResolver, ACCESSIBILITY_BUTTON_TARGETS, SERVICE_NAME_STRING);
+
+        turnOffVolumeKeyShortcutForA11yService(/* alwaysOnService= */ true);
+
+        assertThat(
+                Settings.Secure.getString(mContentResolver, ENABLED_ACCESSIBILITY_SERVICES)
+        ).isEqualTo(SERVICE_NAME_STRING);
+    }
+
+    @Test
+    public void turnOffVolumeShortcutForStandardA11yService_shouldNotTurnOffA11yService()
+            throws Exception {
+        turnOffVolumeKeyShortcutForA11yService(/* alwaysOnService= */ false);
+
+        assertThat(
+                Settings.Secure.getString(mContentResolver, ENABLED_ACCESSIBILITY_SERVICES)
+        ).isEqualTo(SERVICE_NAME_STRING);
+    }
+
+    @Test
     public void testClickingTurnOnButtonInDialog_shouldLeaveShortcutReady() throws Exception {
         configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
         configureValidShortcutService();
@@ -746,6 +793,8 @@
     private void configureEnabledService() throws Exception {
         when(mAccessibilityManagerService.getEnabledAccessibilityServiceList(anyInt(), anyInt()))
                 .thenReturn(Collections.singletonList(mServiceInfo));
+        Settings.Secure.putString(
+                mContentResolver, ENABLED_ACCESSIBILITY_SERVICES, SERVICE_NAME_STRING);
     }
 
     private AccessibilityShortcutController getController() {
@@ -763,4 +812,21 @@
         when(mResources.getString(R.string.config_defaultAccessibilityService)).thenReturn(
                 SERVICE_NAME_STRING);
     }
+
+    private void turnOffVolumeKeyShortcutForA11yService(boolean alwaysOnService) throws Exception {
+        configureValidShortcutService();
+        Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN,
+                AccessibilityShortcutController.DialogStatus.NOT_SHOWN);
+        if (alwaysOnService) {
+            configureRequestAccessibilityButton();
+        }
+        configureEnabledService();
+        getController().performAccessibilityShortcut();
+
+        ArgumentCaptor<DialogInterface.OnClickListener> captor =
+                ArgumentCaptor.forClass(DialogInterface.OnClickListener.class);
+        verify(mAlertDialogBuilder).setPositiveButton(eq(R.string.accessibility_shortcut_off),
+                captor.capture());
+        captor.getValue().onClick(null, DialogInterface.BUTTON_POSITIVE);
+    }
 }
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/TestUtils.java b/core/tests/coretests/src/com/android/internal/accessibility/TestUtils.java
index ff014ad..1bb03fcc 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/TestUtils.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/TestUtils.java
@@ -16,6 +16,17 @@
 
 package com.android.internal.accessibility;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.content.ComponentName;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.os.Build;
+
 import com.android.internal.os.RoSystemProperties;
 
 import java.lang.reflect.Field;
@@ -38,4 +49,32 @@
             throw new RuntimeException(e);
         }
     }
+
+    /**
+     * Creates fake accessibility service info.
+     */
+    public static AccessibilityServiceInfo createFakeServiceInfo(
+            String packageLabel, String serviceComponent,
+            String serviceSummary, boolean isAlwaysOnService) {
+        ApplicationInfo applicationInfo = mock(ApplicationInfo.class);
+        applicationInfo.targetSdkVersion = Build.VERSION_CODES.R;
+        ServiceInfo serviceInfo = mock(ServiceInfo.class);
+        ResolveInfo resolveInfo = mock(ResolveInfo.class);
+        resolveInfo.serviceInfo = serviceInfo;
+        resolveInfo.serviceInfo.applicationInfo = applicationInfo;
+        when(resolveInfo.loadLabel(any())).thenReturn(packageLabel);
+
+        AccessibilityServiceInfo a11yServiceInfo = mock(AccessibilityServiceInfo.class);
+        when(a11yServiceInfo.getResolveInfo()).thenReturn(resolveInfo);
+        when(a11yServiceInfo.getComponentName())
+                .thenReturn(ComponentName.unflattenFromString(serviceComponent));
+        when(a11yServiceInfo.loadSummary(any())).thenReturn(serviceSummary);
+
+        if (isAlwaysOnService) {
+            a11yServiceInfo.flags |= AccessibilityServiceInfo
+                    .FLAG_REQUEST_ACCESSIBILITY_BUTTON;
+        }
+
+        return a11yServiceInfo;
+    }
 }
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/dialog/InvisibleToggleAccessibilityServiceTargetTest.java b/core/tests/coretests/src/com/android/internal/accessibility/dialog/InvisibleToggleAccessibilityServiceTargetTest.java
new file mode 100644
index 0000000..69b6a9b7a
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/accessibility/dialog/InvisibleToggleAccessibilityServiceTargetTest.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.accessibility.dialog;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.ParceledListSlice;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.IAccessibilityManager;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.accessibility.TestUtils;
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.internal.util.test.FakeSettingsProviderRule;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Collections;
+
+/**
+ * Unit Tests for
+ * {@link com.android.internal.accessibility.dialog.InvisibleToggleAccessibilityServiceTarget}
+ */
+@RunWith(AndroidJUnit4.class)
+public class InvisibleToggleAccessibilityServiceTargetTest {
+    @Rule
+    public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
+    @Mock
+    private IAccessibilityManager mAccessibilityManagerService;
+
+    private static final String ALWAYS_ON_SERVICE_PACKAGE_LABEL = "always on a11y service";
+    private static final String ALWAYS_ON_SERVICE_COMPONENT_NAME =
+            "fake.package/fake.alwayson.service.name";
+    private static final String FAKE_A11Y_SERVICE_SUMMARY = "A11yService summary";
+
+    private ContextWrapper mContextSpy;
+    private InvisibleToggleAccessibilityServiceTarget mSut;
+
+    @Before
+    public void setUp() throws RemoteException {
+        MockitoAnnotations.initMocks(this);
+        mContextSpy = spy(
+                new ContextWrapper(InstrumentationRegistry.getInstrumentation().getContext()));
+
+        ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
+        when(mContextSpy.getContentResolver()).thenReturn(contentResolver);
+
+        AccessibilityManager accessibilityManager =
+                new AccessibilityManager(
+                        mContextSpy, mock(Handler.class),
+                        mAccessibilityManagerService, UserHandle.myUserId(),
+                        /* serviceConnect= */ true);
+        when(mContextSpy.getSystemService(Context.ACCESSIBILITY_SERVICE))
+                .thenReturn(accessibilityManager);
+
+        AccessibilityServiceInfo accessibilityServiceInfo = TestUtils.createFakeServiceInfo(
+                ALWAYS_ON_SERVICE_PACKAGE_LABEL,
+                ALWAYS_ON_SERVICE_COMPONENT_NAME,
+                FAKE_A11Y_SERVICE_SUMMARY,
+                /* isAlwaysOnService*/ true);
+        when(mAccessibilityManagerService.getInstalledAccessibilityServiceList(anyInt()))
+                .thenReturn(
+                        new ParceledListSlice<>(
+                                Collections.singletonList(accessibilityServiceInfo)));
+
+        mSut = new InvisibleToggleAccessibilityServiceTarget(
+                mContextSpy,
+                AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY, accessibilityServiceInfo);
+    }
+
+    @Test
+    public void onCheckedChanged_turnOnShortcut_hasOtherShortcut_serviceKeepsOn() {
+        enableA11yService(/* enable= */ true);
+        addShortcutForA11yService(
+                Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, /* add= */ false);
+        addShortcutForA11yService(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, /* add= */ true);
+
+        mSut.onCheckedChanged(/* isChecked= */ true);
+
+        assertA11yServiceState(/* enabled= */ true);
+    }
+
+    @Test
+    public void onCheckedChanged_turnOnShortcut_noOtherShortcut_shouldTurnOnService() {
+        enableA11yService(/* enable= */ false);
+        addShortcutForA11yService(
+                Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, /* add= */ false);
+        addShortcutForA11yService(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, /* add= */ false);
+
+        mSut.onCheckedChanged(/* isChecked= */ true);
+
+        assertA11yServiceState(/* enabled= */ true);
+    }
+
+    @Test
+    public void onCheckedChanged_turnOffShortcut_hasOtherShortcut_serviceKeepsOn() {
+        enableA11yService(/* enable= */ true);
+        addShortcutForA11yService(
+                Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, /* add= */ true);
+        addShortcutForA11yService(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, /* add= */ true);
+
+        mSut.onCheckedChanged(/* isChecked= */ false);
+
+        assertA11yServiceState(/* enabled= */ true);
+    }
+
+    @Test
+    public void onCheckedChanged_turnOffShortcut_noOtherShortcut_shouldTurnOffService() {
+        enableA11yService(/* enable= */ true);
+        addShortcutForA11yService(
+                Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, /* add= */ true);
+        addShortcutForA11yService(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, /* add= */ false);
+
+        mSut.onCheckedChanged(/* isChecked= */ false);
+
+        assertA11yServiceState(/* enabled= */ false);
+    }
+
+    private void enableA11yService(boolean enable) {
+        Settings.Secure.putString(
+                mContextSpy.getContentResolver(),
+                Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+                enable ? ALWAYS_ON_SERVICE_COMPONENT_NAME : "");
+    }
+
+    private void addShortcutForA11yService(String shortcutKey, boolean add) {
+        Settings.Secure.putString(
+                mContextSpy.getContentResolver(),
+                shortcutKey,
+                add ? ALWAYS_ON_SERVICE_COMPONENT_NAME : "");
+    }
+
+    private void assertA11yServiceState(boolean enabled) {
+        if (enabled) {
+            assertThat(
+                    Settings.Secure.getString(
+                            mContextSpy.getContentResolver(),
+                            Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES)
+            ).contains(ALWAYS_ON_SERVICE_COMPONENT_NAME);
+        } else {
+            assertThat(
+                    Settings.Secure.getString(
+                            mContextSpy.getContentResolver(),
+                            Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES)
+            ).doesNotContain(ALWAYS_ON_SERVICE_COMPONENT_NAME);
+        }
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityUtilsTest.java b/core/tests/coretests/src/com/android/internal/accessibility/util/AccessibilityUtilsTest.java
similarity index 97%
rename from core/tests/coretests/src/com/android/internal/accessibility/AccessibilityUtilsTest.java
rename to core/tests/coretests/src/com/android/internal/accessibility/util/AccessibilityUtilsTest.java
index 3ea7f47..58ab92a 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/util/AccessibilityUtilsTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.internal.accessibility;
+package com.android.internal.accessibility.util;
 
 import static com.android.internal.accessibility.util.AccessibilityUtils.ACCESSIBILITY_MENU_IN_SYSTEM;
 import static com.android.internal.accessibility.util.AccessibilityUtils.MENU_SERVICE_RELATIVE_CLASS_NAME;
@@ -35,8 +35,6 @@
 
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.internal.accessibility.util.AccessibilityUtils;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/util/ShortcutUtilsTest.java b/core/tests/coretests/src/com/android/internal/accessibility/util/ShortcutUtilsTest.java
new file mode 100644
index 0000000..708f246
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/accessibility/util/ShortcutUtilsTest.java
@@ -0,0 +1,358 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.accessibility.util;
+
+import static android.provider.Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED;
+import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_MAGNIFICATION_CONTROLLER;
+
+import static com.android.internal.accessibility.common.ShortcutConstants.SERVICES_SEPARATOR;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.ParceledListSlice;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.IAccessibilityManager;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.accessibility.AccessibilityShortcutController;
+import com.android.internal.accessibility.TestUtils;
+import com.android.internal.accessibility.common.ShortcutConstants;
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.internal.util.test.FakeSettingsProviderRule;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+import java.util.Set;
+import java.util.StringJoiner;
+
+/**
+ * Unit Tests for {@link com.android.internal.accessibility.util.ShortcutUtils}
+ */
+@RunWith(AndroidJUnit4.class)
+public class ShortcutUtilsTest {
+    private static final Set<String> ONE_COMPONENT = Set.of(
+            new ComponentName("pkg", "serv").flattenToString());
+    private static final Set<String> TWO_COMPONENTS = Set.of(
+            new ComponentName("pkg", "serv").flattenToString(),
+            AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME);
+    private static final String ALWAYS_ON_SERVICE_PACKAGE_LABEL = "always on a11y service";
+    private static final String ALWAYS_ON_SERVICE_COMPONENT_NAME =
+            "fake.package/fake.alwayson.service.name";
+
+    private static final String STANDARD_SERVICE_PACKAGE_LABEL = "standard a11y service";
+    private static final String STANDARD_SERVICE_COMPONENT_NAME =
+            "fake.package/fake.standard.service.name";
+    private static final String SERVICE_NAME_SUMMARY = "Summary";
+
+    @Rule
+    public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
+    @Mock
+    private IAccessibilityManager mAccessibilityManagerService;
+    private ContextWrapper mContextSpy;
+
+    @Before
+    public void setUp() throws RemoteException {
+        MockitoAnnotations.initMocks(this);
+        mContextSpy = spy(
+                new ContextWrapper(InstrumentationRegistry.getInstrumentation().getContext()));
+
+        ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
+        when(mContextSpy.getContentResolver()).thenReturn(contentResolver);
+
+        AccessibilityManager accessibilityManager =
+                new AccessibilityManager(
+                        mContextSpy, mock(Handler.class),
+                        mAccessibilityManagerService, UserHandle.myUserId(),
+                        /* serviceConnect= */ true);
+        when(mContextSpy.getSystemService(Context.ACCESSIBILITY_SERVICE))
+                .thenReturn(accessibilityManager);
+
+        setupFakeA11yServiceInfos();
+    }
+
+    @Test
+    public void getShortcutTargets_softwareShortcutNoService_emptyResult() {
+        assertThat(
+                ShortcutUtils.getShortcutTargetsFromSettings(
+                        mContextSpy,
+                        ShortcutConstants.UserShortcutType.SOFTWARE, UserHandle.myUserId())
+        ).isEmpty();
+    }
+
+    @Test
+    public void getShortcutTargets_volumeKeyShortcutNoService_emptyResult() {
+        assertThat(
+                ShortcutUtils.getShortcutTargetsFromSettings(
+                        mContextSpy, ShortcutConstants.UserShortcutType.HARDWARE,
+                        UserHandle.myUserId())
+        ).isEmpty();
+    }
+
+    @Test
+    public void getShortcutTargets_softwareShortcut1Service_return1Service() {
+        setupShortcutTargets(ONE_COMPONENT, Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
+        setupShortcutTargets(TWO_COMPONENTS, Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
+
+        assertThat(
+                ShortcutUtils.getShortcutTargetsFromSettings(
+                        mContextSpy, ShortcutConstants.UserShortcutType.SOFTWARE,
+                        UserHandle.myUserId())
+        ).containsExactlyElementsIn(ONE_COMPONENT);
+    }
+
+    @Test
+    public void getShortcutTargets_volumeShortcut2Service_return2Service() {
+        setupShortcutTargets(ONE_COMPONENT, Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
+        setupShortcutTargets(TWO_COMPONENTS, Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
+
+        assertThat(
+                ShortcutUtils.getShortcutTargetsFromSettings(
+                        mContextSpy, ShortcutConstants.UserShortcutType.HARDWARE,
+                        UserHandle.myUserId())
+        ).containsExactlyElementsIn(TWO_COMPONENTS);
+    }
+
+    @Test
+    public void getShortcutTargets_tripleTapShortcut_magnificationDisabled_emptyResult() {
+        enableTripleTapShortcutForMagnification(/* enable= */ false);
+        setupShortcutTargets(ONE_COMPONENT, Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
+        setupShortcutTargets(TWO_COMPONENTS, Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
+
+        assertThat(
+                ShortcutUtils.getShortcutTargetsFromSettings(
+                        mContextSpy, ShortcutConstants.UserShortcutType.TRIPLETAP,
+                        UserHandle.myUserId())
+        ).isEmpty();
+    }
+
+    @Test
+    public void getShortcutTargets_tripleTapShortcut_magnificationEnabled_returnMagnification() {
+        setupShortcutTargets(ONE_COMPONENT, Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
+        setupShortcutTargets(TWO_COMPONENTS, Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
+        enableTripleTapShortcutForMagnification(/* enable= */ true);
+
+        assertThat(
+                ShortcutUtils.getShortcutTargetsFromSettings(
+                        mContextSpy, ShortcutConstants.UserShortcutType.TRIPLETAP,
+                        UserHandle.myUserId())
+        ).containsExactly(ACCESSIBILITY_SHORTCUT_TARGET_MAGNIFICATION_CONTROLLER);
+    }
+
+    @Test
+    public void updateAccessibilityServiceStateIfNeeded_alwaysOnServiceOn_noShortcuts_serviceTurnedOff() {
+        setupA11yServiceAndShortcutState(
+                ALWAYS_ON_SERVICE_COMPONENT_NAME, /* serviceOn= */ true, /* shortcutOn= */ false);
+
+        ShortcutUtils.updateInvisibleToggleAccessibilityServiceEnableState(
+                mContextSpy,
+                Set.of(ALWAYS_ON_SERVICE_COMPONENT_NAME),
+                UserHandle.myUserId()
+        );
+
+        assertA11yServiceState(ALWAYS_ON_SERVICE_COMPONENT_NAME, /* enabled= */ false);
+    }
+
+    @Test
+    public void updateAccessibilityServiceStateIfNeeded_alwaysOnServiceOn_hasShortcut_serviceKeepsOn() {
+        setupA11yServiceAndShortcutState(
+                ALWAYS_ON_SERVICE_COMPONENT_NAME, /* serviceOn= */ true, /* shortcutOn= */ true);
+
+        ShortcutUtils.updateInvisibleToggleAccessibilityServiceEnableState(
+                mContextSpy,
+                Set.of(ALWAYS_ON_SERVICE_COMPONENT_NAME),
+                UserHandle.myUserId()
+        );
+
+        assertA11yServiceState(ALWAYS_ON_SERVICE_COMPONENT_NAME, /* enabled= */ true);
+    }
+
+    @Test
+    public void updateAccessibilityServiceStateIfNeeded_alwaysOnServiceOff_noShortcuts_serviceKeepsOff() {
+        setupA11yServiceAndShortcutState(
+                ALWAYS_ON_SERVICE_COMPONENT_NAME, /* serviceOn= */ false, /* shortcutOn= */ false);
+
+        ShortcutUtils.updateInvisibleToggleAccessibilityServiceEnableState(
+                mContextSpy,
+                Set.of(ALWAYS_ON_SERVICE_COMPONENT_NAME),
+                UserHandle.myUserId()
+        );
+
+        assertA11yServiceState(ALWAYS_ON_SERVICE_COMPONENT_NAME, /* enabled= */ false);
+    }
+
+    @Test
+    public void updateAccessibilityServiceStateIfNeeded_alwaysOnServiceOff_hasShortcuts_serviceTurnsOn() {
+        setupA11yServiceAndShortcutState(
+                ALWAYS_ON_SERVICE_COMPONENT_NAME, /* serviceOn= */ false, /* shortcutOn= */ true);
+
+        ShortcutUtils.updateInvisibleToggleAccessibilityServiceEnableState(
+                mContextSpy,
+                Set.of(ALWAYS_ON_SERVICE_COMPONENT_NAME),
+                UserHandle.myUserId()
+        );
+
+        assertA11yServiceState(ALWAYS_ON_SERVICE_COMPONENT_NAME, /* enabled= */ true);
+    }
+
+    @Test
+    public void updateAccessibilityServiceStateIfNeeded_standardA11yServiceOn_noShortcuts_serviceKeepsOn() {
+        setupA11yServiceAndShortcutState(
+                STANDARD_SERVICE_COMPONENT_NAME, /* serviceOn= */ true, /* shortcutOn= */ false);
+
+        ShortcutUtils.updateInvisibleToggleAccessibilityServiceEnableState(
+                mContextSpy,
+                Set.of(STANDARD_SERVICE_COMPONENT_NAME),
+                UserHandle.myUserId()
+        );
+
+        assertA11yServiceState(STANDARD_SERVICE_COMPONENT_NAME, /* enabled= */ true);
+    }
+
+    @Test
+    public void updateAccessibilityServiceStateIfNeeded_standardA11yServiceOn_hasShortcuts_serviceKeepsOn() {
+        setupA11yServiceAndShortcutState(
+                STANDARD_SERVICE_COMPONENT_NAME, /* serviceOn= */ true, /* shortcutOn= */ true);
+
+        ShortcutUtils.updateInvisibleToggleAccessibilityServiceEnableState(
+                mContextSpy,
+                Set.of(STANDARD_SERVICE_COMPONENT_NAME),
+                UserHandle.myUserId()
+        );
+
+        assertA11yServiceState(STANDARD_SERVICE_COMPONENT_NAME, /* enabled= */ true);
+    }
+
+    @Test
+    public void updateAccessibilityServiceStateIfNeeded_standardA11yServiceOff_noShortcuts_serviceKeepsOff() {
+        setupA11yServiceAndShortcutState(
+                STANDARD_SERVICE_COMPONENT_NAME, /* serviceOn= */ false, /* shortcutOn= */ false);
+
+        ShortcutUtils.updateInvisibleToggleAccessibilityServiceEnableState(
+                mContextSpy,
+                Set.of(STANDARD_SERVICE_COMPONENT_NAME),
+                UserHandle.myUserId()
+        );
+
+        assertA11yServiceState(STANDARD_SERVICE_COMPONENT_NAME, /* enabled= */ false);
+    }
+
+    @Test
+    public void updateAccessibilityServiceStateIfNeeded_standardA11yServiceOff_hasShortcuts_serviceKeepsOff() {
+        setupA11yServiceAndShortcutState(
+                STANDARD_SERVICE_COMPONENT_NAME, /* serviceOn= */ false, /* shortcutOn= */ true);
+
+        ShortcutUtils.updateInvisibleToggleAccessibilityServiceEnableState(
+                mContextSpy,
+                Set.of(STANDARD_SERVICE_COMPONENT_NAME),
+                UserHandle.myUserId()
+        );
+
+        assertA11yServiceState(STANDARD_SERVICE_COMPONENT_NAME, /* enabled= */ false);
+    }
+
+    private void setupShortcutTargets(Set<String> components, String shortcutSettingsKey) {
+        final StringJoiner stringJoiner = new StringJoiner(String.valueOf(SERVICES_SEPARATOR));
+        for (String target : components) {
+            stringJoiner.add(target);
+        }
+        Settings.Secure.putStringForUser(
+                mContextSpy.getContentResolver(), shortcutSettingsKey,
+                stringJoiner.toString(),
+                UserHandle.myUserId());
+    }
+
+    private void enableTripleTapShortcutForMagnification(boolean enable) {
+        Settings.Secure.putInt(
+                mContextSpy.getContentResolver(),
+                ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, enable ? 1 : 0);
+    }
+
+    private void setupFakeA11yServiceInfos() throws RemoteException {
+        List<AccessibilityServiceInfo> serviceInfos = List.of(
+                TestUtils.createFakeServiceInfo(
+                        ALWAYS_ON_SERVICE_PACKAGE_LABEL,
+                        ALWAYS_ON_SERVICE_COMPONENT_NAME,
+                        SERVICE_NAME_SUMMARY,
+                        /* isAlwaysOnService*/ true),
+                TestUtils.createFakeServiceInfo(
+                        STANDARD_SERVICE_PACKAGE_LABEL,
+                        STANDARD_SERVICE_COMPONENT_NAME,
+                        SERVICE_NAME_SUMMARY,
+                        /* isAlwaysOnService*/ false)
+        );
+        when(mAccessibilityManagerService.getInstalledAccessibilityServiceList(anyInt()))
+                .thenReturn(new ParceledListSlice<>(serviceInfos));
+    }
+
+    private void setupA11yServiceAndShortcutState(
+            String a11yServiceComponentName, boolean serviceOn, boolean shortcutOn) {
+        enableA11yService(a11yServiceComponentName, serviceOn);
+        addShortcutForA11yService(a11yServiceComponentName, shortcutOn);
+    }
+
+    private void assertA11yServiceState(String a11yServiceComponentName, boolean enabled) {
+        if (enabled) {
+            assertThat(
+                    Settings.Secure.getString(
+                            mContextSpy.getContentResolver(),
+                            Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES)
+            ).contains(a11yServiceComponentName);
+        } else {
+            assertThat(
+                    Settings.Secure.getString(
+                            mContextSpy.getContentResolver(),
+                            Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES)
+            ).doesNotContain(a11yServiceComponentName);
+        }
+    }
+
+    private void enableA11yService(String a11yServiceComponentName, boolean enable) {
+        Settings.Secure.putString(
+                mContextSpy.getContentResolver(),
+                Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+                enable ? a11yServiceComponentName : "");
+    }
+
+    private void addShortcutForA11yService(String a11yServiceComponentName, boolean add) {
+        Settings.Secure.putString(
+                mContextSpy.getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
+                add ? a11yServiceComponentName : "");
+    }
+}
diff --git a/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorTest.java b/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorTest.java
index a339907..8e653f5 100644
--- a/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorTest.java
+++ b/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorTest.java
@@ -298,6 +298,43 @@
     }
 
     @Test
+    public void testPackageMonitorDoHandlePackageEventPackageRemovedReplacingArchived() {
+        PackageMonitor spyPackageMonitor = spy(new TestPackageMonitor());
+
+        Intent intent = new Intent(Intent.ACTION_PACKAGE_REMOVED);
+        intent.setData(Uri.fromParts("package", FAKE_PACKAGE_NAME, null));
+        intent.putExtra(Intent.EXTRA_USER_HANDLE, FAKE_USER_ID);
+        intent.putExtra(Intent.EXTRA_UID, FAKE_PACKAGE_UID);
+        intent.putExtra(Intent.EXTRA_REPLACING, true);
+        intent.putExtra(Intent.EXTRA_ARCHIVAL, true);
+        intent.putExtra(Intent.EXTRA_REMOVED_FOR_ALL_USERS, true);
+        spyPackageMonitor.doHandlePackageEvent(intent);
+
+        verify(spyPackageMonitor, times(1)).onBeginPackageChanges();
+        verify(spyPackageMonitor, times(1))
+                .onPackageUpdateStarted(eq(FAKE_PACKAGE_NAME), eq(FAKE_PACKAGE_UID));
+        verify(spyPackageMonitor, times(1)).onPackageModified(eq(FAKE_PACKAGE_NAME));
+
+        ArgumentCaptor<Bundle> argumentCaptor = ArgumentCaptor.forClass(Bundle.class);
+        verify(spyPackageMonitor, times(1))
+                .onPackageDisappearedWithExtras(eq(FAKE_PACKAGE_NAME), argumentCaptor.capture());
+        Bundle capturedExtras = argumentCaptor.getValue();
+        Bundle expectedExtras = intent.getExtras();
+        assertThat(capturedExtras.getInt(Intent.EXTRA_USER_HANDLE))
+                .isEqualTo(expectedExtras.getInt(Intent.EXTRA_USER_HANDLE));
+        assertThat(capturedExtras.getInt(Intent.EXTRA_UID))
+                .isEqualTo(expectedExtras.getInt(Intent.EXTRA_UID));
+        assertThat(capturedExtras.getInt(Intent.EXTRA_REPLACING))
+                .isEqualTo(expectedExtras.getInt(Intent.EXTRA_REPLACING));
+        assertThat(capturedExtras.getInt(Intent.EXTRA_REMOVED_FOR_ALL_USERS))
+                .isEqualTo(expectedExtras.getInt(Intent.EXTRA_REMOVED_FOR_ALL_USERS));
+
+        verify(spyPackageMonitor, times(1))
+                .onPackageDisappeared(eq(FAKE_PACKAGE_NAME), eq(PackageMonitor.PACKAGE_UPDATING));
+        verify(spyPackageMonitor, times(1)).onFinishPackageChanges();
+    }
+
+    @Test
     public void testPackageMonitorDoHandlePackageEventPackageRemovedNotReplacing()
             throws Exception {
         PackageMonitor spyPackageMonitor = spy(new TestPackageMonitor());
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 87be13a..dc2b056 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -2653,6 +2653,12 @@
       "group": "WM_DEBUG_WINDOW_TRANSITIONS",
       "at": "com\/android\/server\/wm\/TransitionController.java"
     },
+    "261227010": {
+      "message": "Content Recording: Unable to tell log windowing mode change: %s",
+      "level": "ERROR",
+      "group": "WM_DEBUG_CONTENT_RECORDING",
+      "at": "com\/android\/server\/wm\/ContentRecorder.java"
+    },
     "269576220": {
       "message": "Resuming rotation after drag",
       "level": "DEBUG",
diff --git a/graphics/java/android/graphics/text/LineBreakConfig.java b/graphics/java/android/graphics/text/LineBreakConfig.java
index 62195856..c5e451a 100644
--- a/graphics/java/android/graphics/text/LineBreakConfig.java
+++ b/graphics/java/android/graphics/text/LineBreakConfig.java
@@ -28,6 +28,8 @@
 import android.compat.annotation.EnabledSince;
 import android.os.Build;
 import android.os.LocaleList;
+import android.os.Parcel;
+import android.os.Parcelable;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -40,7 +42,7 @@
  * <a href="https://www.w3.org/TR/css-text-3/#line-break-property" class="external">
  * line-break property</a> for more information.
  */
-public final class LineBreakConfig {
+public final class LineBreakConfig implements Parcelable {
 
     /**
      * A feature ID for automatic line break word style.
@@ -161,12 +163,12 @@
      *
      * This is useful when you want to preserve some words in the same line by using
      * {@link android.text.style.LineBreakConfigSpan} or
-     * {@link android.text.style.LineBreakConfigSpan.NoBreakSpan} as a shorthand.
+     * {@link android.text.style.LineBreakConfigSpan#createNoBreakSpan()} as a shorthand.
      * Note that even if this style is specified, the grapheme based line break is still performed
      * for preventing clipping text.
      *
      * @see android.text.style.LineBreakConfigSpan
-     * @see android.text.style.LineBreakConfigSpan.NoBreakSpan
+     * @see android.text.style.LineBreakConfigSpan#createNoBreakSpan()
      */
     @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN)
     public static final int LINE_BREAK_STYLE_NO_BREAK = 4;
@@ -457,8 +459,9 @@
      *
      * <p>Use {@link LineBreakConfig.Builder} to create the
      * {@code LineBreakConfig} instance.
+     * @hide
      */
-    private LineBreakConfig(@LineBreakStyle int lineBreakStyle,
+    public LineBreakConfig(@LineBreakStyle int lineBreakStyle,
             @LineBreakWordStyle int lineBreakWordStyle,
             @Hyphenation int hyphenation) {
         mLineBreakStyle = lineBreakStyle;
@@ -606,4 +609,35 @@
                 + ", mHyphenation= " + mHyphenation
                 + '}';
     }
+
+    @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN)
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN)
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mLineBreakStyle);
+        dest.writeInt(mLineBreakWordStyle);
+        dest.writeInt(mHyphenation);
+    }
+
+    @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN)
+    public static final @NonNull Creator<LineBreakConfig> CREATOR = new Creator<>() {
+
+        @Override
+        public LineBreakConfig createFromParcel(Parcel source) {
+            final int lineBreakStyle = source.readInt();
+            final int lineBreakWordStyle = source.readInt();
+            final int hyphenation = source.readInt();
+            return new LineBreakConfig(lineBreakStyle, lineBreakWordStyle, hyphenation);
+        }
+
+        @Override
+        public LineBreakConfig[] newArray(int size) {
+            return new LineBreakConfig[size];
+        }
+    };
 }
diff --git a/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml b/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
index 10c9562..d8ae9c8 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
@@ -26,7 +26,8 @@
         android:id="@+id/bubble_manage_menu_dismiss_container"
         android:background="@drawable/bubble_manage_menu_row"
         android:layout_width="match_parent"
-        android:layout_height="@dimen/bubble_menu_item_height"
+        android:layout_height="wrap_content"
+        android:minHeight="@dimen/bubble_menu_item_height"
         android:gravity="center_vertical"
         android:paddingStart="@dimen/bubble_menu_padding"
         android:paddingEnd="@dimen/bubble_menu_padding"
@@ -52,7 +53,8 @@
         android:id="@+id/bubble_manage_menu_dont_bubble_container"
         android:background="@drawable/bubble_manage_menu_row"
         android:layout_width="match_parent"
-        android:layout_height="@dimen/bubble_menu_item_height"
+        android:layout_height="wrap_content"
+        android:minHeight="@dimen/bubble_menu_item_height"
         android:gravity="center_vertical"
         android:paddingStart="@dimen/bubble_menu_padding"
         android:paddingEnd="@dimen/bubble_menu_padding"
@@ -78,7 +80,8 @@
         android:id="@+id/bubble_manage_menu_settings_container"
         android:background="@drawable/bubble_manage_menu_row"
         android:layout_width="match_parent"
-        android:layout_height="@dimen/bubble_menu_item_height"
+        android:layout_height="wrap_content"
+        android:minHeight="@dimen/bubble_menu_item_height"
         android:gravity="center_vertical"
         android:paddingStart="@dimen/bubble_menu_padding"
         android:paddingEnd="@dimen/bubble_menu_padding"
diff --git a/libs/WindowManager/Shell/tests/flicker/Android.bp b/libs/WindowManager/Shell/tests/flicker/Android.bp
index bd2eb5b..366f7b1 100644
--- a/libs/WindowManager/Shell/tests/flicker/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/Android.bp
@@ -28,93 +28,6 @@
     srcs: ["src/com/android/wm/shell/flicker/utils/*.kt"],
 }
 
-filegroup {
-    name: "WMShellFlickerTestsBase-src",
-    srcs: ["src/com/android/wm/shell/flicker/*.kt"],
-}
-
-filegroup {
-    name: "WMShellFlickerTestsBubbles-src",
-    srcs: ["src/com/android/wm/shell/flicker/bubble/*.kt"],
-}
-
-filegroup {
-    name: "WMShellFlickerTestsPip1-src",
-    srcs: [
-        "src/com/android/wm/shell/flicker/pip/A*.kt",
-        "src/com/android/wm/shell/flicker/pip/B*.kt",
-        "src/com/android/wm/shell/flicker/pip/C*.kt",
-        "src/com/android/wm/shell/flicker/pip/D*.kt",
-        "src/com/android/wm/shell/flicker/pip/S*.kt",
-    ],
-}
-
-filegroup {
-    name: "WMShellFlickerTestsPip2-src",
-    srcs: [
-        "src/com/android/wm/shell/flicker/pip/E*.kt",
-    ],
-}
-
-filegroup {
-    name: "WMShellFlickerTestsPip3-src",
-    srcs: ["src/com/android/wm/shell/flicker/pip/*.kt"],
-}
-
-filegroup {
-    name: "WMShellFlickerTestsPipCommon-src",
-    srcs: ["src/com/android/wm/shell/flicker/pip/common/*.kt"],
-}
-
-filegroup {
-    name: "WMShellFlickerTestsPipApps-src",
-    srcs: ["src/com/android/wm/shell/flicker/pip/apps/*.kt"],
-}
-
-filegroup {
-    name: "WMShellFlickerTestsSplitScreenBase-src",
-    srcs: [
-        "src/com/android/wm/shell/flicker/splitscreen/benchmark/*.kt",
-    ],
-}
-
-filegroup {
-    name: "WMShellFlickerTestsSplitScreenGroup1-src",
-    srcs: [
-        "src/com/android/wm/shell/flicker/splitscreen/A*.kt",
-        "src/com/android/wm/shell/flicker/splitscreen/B*.kt",
-        "src/com/android/wm/shell/flicker/splitscreen/C*.kt",
-        "src/com/android/wm/shell/flicker/splitscreen/D*.kt",
-        "src/com/android/wm/shell/flicker/splitscreen/E*.kt",
-    ],
-}
-
-filegroup {
-    name: "WMShellFlickerTestsSplitScreenGroup2-src",
-    srcs: [
-        "src/com/android/wm/shell/flicker/splitscreen/*.kt",
-    ],
-}
-
-filegroup {
-    name: "WMShellFlickerServicePlatinumTests-src",
-    srcs: [
-        "src/com/android/wm/shell/flicker/service/*/platinum/**/*.kt",
-        "src/com/android/wm/shell/flicker/service/*/scenarios/**/*.kt",
-        "src/com/android/wm/shell/flicker/service/common/**/*.kt",
-    ],
-}
-
-filegroup {
-    name: "WMShellFlickerServiceTests-src",
-    srcs: [
-        "src/com/android/wm/shell/flicker/service/**/*.kt",
-    ],
-    exclude_srcs: [
-        "src/com/android/wm/shell/flicker/service/*/platinum/**/*.kt",
-    ],
-}
-
 java_library {
     name: "wm-shell-flicker-utils",
     platform_apis: true,
@@ -138,23 +51,8 @@
     ],
 }
 
-java_library {
-    name: "wm-shell-flicker-platinum-tests",
-    platform_apis: true,
-    optimize: {
-        enabled: false,
-    },
-    srcs: [
-        ":WMShellFlickerServicePlatinumTests-src",
-    ],
-    static_libs: [
-        "wm-shell-flicker-utils",
-    ],
-}
-
 java_defaults {
     name: "WMShellFlickerTestsDefaultWithoutTemplate",
-    manifest: "manifests/AndroidManifest.xml",
     platform_apis: true,
     certificate: "platform",
     optimize: {
@@ -187,170 +85,8 @@
     test_config_template: "AndroidTestTemplate.xml",
 }
 
-android_test {
-    name: "WMShellFlickerTestsOther",
+java_library {
+    name: "WMShellFlickerTestsBase",
     defaults: ["WMShellFlickerTestsDefault"],
-    additional_manifests: ["manifests/AndroidManifestOther.xml"],
-    package_name: "com.android.wm.shell.flicker",
-    instrumentation_target_package: "com.android.wm.shell.flicker",
-    srcs: [
-        "src/**/*.java",
-        "src/**/*.kt",
-    ],
-    exclude_srcs: [
-        ":WMShellFlickerTestsBubbles-src",
-        ":WMShellFlickerTestsPip1-src",
-        ":WMShellFlickerTestsPip2-src",
-        ":WMShellFlickerTestsPip3-src",
-        ":WMShellFlickerTestsPipCommon-src",
-        ":WMShellFlickerTestsPipApps-src",
-        ":WMShellFlickerTestsSplitScreenGroup1-src",
-        ":WMShellFlickerTestsSplitScreenGroup2-src",
-        ":WMShellFlickerTestsSplitScreenBase-src",
-        ":WMShellFlickerServiceTests-src",
-        ":WMShellFlickerServicePlatinumTests-src",
-    ],
-}
-
-android_test {
-    name: "WMShellFlickerTestsBubbles",
-    defaults: ["WMShellFlickerTestsDefault"],
-    additional_manifests: ["manifests/AndroidManifestBubbles.xml"],
-    package_name: "com.android.wm.shell.flicker.bubbles",
-    instrumentation_target_package: "com.android.wm.shell.flicker.bubbles",
-    srcs: [
-        ":WMShellFlickerTestsBase-src",
-        ":WMShellFlickerTestsBubbles-src",
-    ],
-}
-
-android_test {
-    name: "WMShellFlickerTestsPip1",
-    defaults: ["WMShellFlickerTestsDefault"],
-    additional_manifests: ["manifests/AndroidManifestPip.xml"],
-    package_name: "com.android.wm.shell.flicker.pip",
-    instrumentation_target_package: "com.android.wm.shell.flicker.pip",
-    srcs: [
-        ":WMShellFlickerTestsBase-src",
-        ":WMShellFlickerTestsPip1-src",
-        ":WMShellFlickerTestsPipCommon-src",
-    ],
-}
-
-android_test {
-    name: "WMShellFlickerTestsPip2",
-    defaults: ["WMShellFlickerTestsDefault"],
-    additional_manifests: ["manifests/AndroidManifestPip.xml"],
-    package_name: "com.android.wm.shell.flicker.pip",
-    instrumentation_target_package: "com.android.wm.shell.flicker.pip",
-    srcs: [
-        ":WMShellFlickerTestsBase-src",
-        ":WMShellFlickerTestsPip2-src",
-        ":WMShellFlickerTestsPipCommon-src",
-    ],
-}
-
-android_test {
-    name: "WMShellFlickerTestsPip3",
-    defaults: ["WMShellFlickerTestsDefault"],
-    additional_manifests: ["manifests/AndroidManifestPip.xml"],
-    package_name: "com.android.wm.shell.flicker.pip",
-    instrumentation_target_package: "com.android.wm.shell.flicker.pip",
-    srcs: [
-        ":WMShellFlickerTestsBase-src",
-        ":WMShellFlickerTestsPip3-src",
-        ":WMShellFlickerTestsPipCommon-src",
-    ],
-    exclude_srcs: [
-        ":WMShellFlickerTestsPip1-src",
-        ":WMShellFlickerTestsPip2-src",
-    ],
-}
-
-android_test {
-    name: "WMShellFlickerTestsPipApps",
-    defaults: ["WMShellFlickerTestsDefault"],
-    additional_manifests: ["manifests/AndroidManifestPip.xml"],
-    package_name: "com.android.wm.shell.flicker.pip.apps",
-    instrumentation_target_package: "com.android.wm.shell.flicker.pip.apps",
-    srcs: [
-        ":WMShellFlickerTestsBase-src",
-        ":WMShellFlickerTestsPipApps-src",
-        ":WMShellFlickerTestsPipCommon-src",
-    ],
-}
-
-android_test {
-    name: "WMShellFlickerTestsPipAppsCSuite",
-    defaults: ["WMShellFlickerTestsDefaultWithoutTemplate"],
-    additional_manifests: ["manifests/AndroidManifestPip.xml"],
-    package_name: "com.android.wm.shell.flicker.pip.apps",
-    instrumentation_target_package: "com.android.wm.shell.flicker.pip.apps",
-    srcs: [
-        ":WMShellFlickerTestsBase-src",
-        ":WMShellFlickerTestsPipApps-src",
-        ":WMShellFlickerTestsPipCommon-src",
-    ],
-    test_suites: [
-        "device-tests",
-        "csuite",
-    ],
-}
-
-android_test {
-    name: "WMShellFlickerTestsSplitScreenGroup1",
-    defaults: ["WMShellFlickerTestsDefault"],
-    additional_manifests: ["manifests/AndroidManifestSplitScreen.xml"],
-    package_name: "com.android.wm.shell.flicker.splitscreen",
-    instrumentation_target_package: "com.android.wm.shell.flicker.splitscreen",
-    srcs: [
-        ":WMShellFlickerTestsBase-src",
-        ":WMShellFlickerTestsSplitScreenBase-src",
-        ":WMShellFlickerTestsSplitScreenGroup1-src",
-    ],
-}
-
-android_test {
-    name: "WMShellFlickerTestsSplitScreenGroup2",
-    defaults: ["WMShellFlickerTestsDefault"],
-    additional_manifests: ["manifests/AndroidManifestSplitScreen.xml"],
-    package_name: "com.android.wm.shell.flicker.splitscreen",
-    instrumentation_target_package: "com.android.wm.shell.flicker.splitscreen",
-    srcs: [
-        ":WMShellFlickerTestsBase-src",
-        ":WMShellFlickerTestsSplitScreenBase-src",
-        ":WMShellFlickerTestsSplitScreenGroup2-src",
-    ],
-    exclude_srcs: [
-        ":WMShellFlickerTestsSplitScreenGroup1-src",
-    ],
-}
-
-android_test {
-    name: "WMShellFlickerServiceTests",
-    defaults: ["WMShellFlickerTestsDefault"],
-    additional_manifests: ["manifests/AndroidManifestService.xml"],
-    package_name: "com.android.wm.shell.flicker.service",
-    instrumentation_target_package: "com.android.wm.shell.flicker.service",
-    srcs: [
-        ":WMShellFlickerTestsBase-src",
-        ":WMShellFlickerServiceTests-src",
-    ],
-}
-
-android_test {
-    name: "WMShellFlickerServicePlatinumTests",
-    defaults: ["WMShellFlickerTestsDefault"],
-    additional_manifests: ["manifests/AndroidManifestService.xml"],
-    package_name: "com.android.wm.shell.flicker.service",
-    instrumentation_target_package: "com.android.wm.shell.flicker.service",
-    srcs: [
-        ":WMShellFlickerTestsBase-src",
-        ":WMShellFlickerServicePlatinumTests-src",
-    ],
-}
-
-csuite_test {
-    name: "csuite-1p3p-pip-flickers",
-    test_config_template: "csuiteDefaultTemplate.xml",
+    srcs: ["src/com/android/wm/shell/flicker/*.kt"],
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/Android.bp b/libs/WindowManager/Shell/tests/flicker/appcompat/Android.bp
new file mode 100644
index 0000000..bae701f
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/appcompat/Android.bp
@@ -0,0 +1,41 @@
+//
+// Copyright (C) 2020 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 {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+filegroup {
+    name: "WMShellFlickerTestsAppCompat-src",
+    srcs: [
+        "src/**/*.kt",
+    ],
+}
+
+android_test {
+    name: "WMShellFlickerTestsOther",
+    defaults: ["WMShellFlickerTestsDefault"],
+    manifest: "AndroidManifest.xml",
+    package_name: "com.android.wm.shell.flicker",
+    instrumentation_target_package: "com.android.wm.shell.flicker",
+    srcs: [":WMShellFlickerTestsAppCompat-src"],
+    static_libs: ["WMShellFlickerTestsBase"],
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidManifest.xml
similarity index 77%
rename from libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml
rename to libs/WindowManager/Shell/tests/flicker/appcompat/AndroidManifest.xml
index ae130b8..2af1e74 100644
--- a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidManifest.xml
@@ -1,18 +1,18 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 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) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           xmlns:tools="http://schemas.android.com/tools"
@@ -69,4 +69,9 @@
             android:authorities="${applicationId}.androidx-startup"
             tools:node="remove" />
     </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.wm.shell.flicker"
+                     android:label="WindowManager Flicker Tests">
+    </instrumentation>
 </manifest>
diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml
new file mode 100644
index 0000000..1df1136
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<configuration description="Runs WindowManager Shell Flicker Tests {MODULE}">
+    <option name="test-tag" value="FlickerTests"/>
+    <!-- Needed for storing the perfetto trace files in the sdcard/test_results-->
+    <option name="isolated-storage" value="false"/>
+
+    <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+        <!-- keeps the screen on during tests -->
+        <option name="screen-always-on" value="on"/>
+        <!-- prevents the phone from restarting -->
+        <option name="force-skip-system-props" value="true"/>
+        <!-- set WM tracing verbose level to all -->
+        <option name="run-command" value="cmd window tracing level all"/>
+        <!-- set WM tracing to frame (avoid incomplete states) -->
+        <option name="run-command" value="cmd window tracing frame"/>
+        <!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests -->
+        <option name="run-command" value="pm disable com.google.android.internal.betterbug"/>
+        <!-- ensure lock screen mode is swipe -->
+        <option name="run-command" value="locksettings set-disabled false"/>
+        <!-- restart launcher to activate TAPL -->
+        <option name="run-command"
+                value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher"/>
+        <!-- Increase trace size: 20mb for WM and 80mb for SF -->
+        <option name="run-command" value="cmd window tracing size 20480"/>
+        <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/>
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <option name="test-user-token" value="%TEST_USER%"/>
+        <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
+        <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
+        <option name="run-command" value="settings put system show_touches 1"/>
+        <option name="run-command" value="settings put system pointer_location 1"/>
+        <option name="teardown-command"
+                value="settings delete secure show_ime_with_hard_keyboard"/>
+        <option name="teardown-command" value="settings delete system show_touches"/>
+        <option name="teardown-command" value="settings delete system pointer_location"/>
+        <option name="teardown-command"
+                value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/>
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true"/>
+        <option name="test-file-name" value="{MODULE}.apk"/>
+        <option name="test-file-name" value="FlickerTestApp.apk"/>
+    </target_preparer>
+    <!-- Enable mocking GPS location by the test app -->
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <option name="run-command"
+                value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location allow"/>
+        <option name="teardown-command"
+                value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location deny"/>
+    </target_preparer>
+
+    <!-- Needed for pushing the trace config file -->
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="push-file"
+                key="trace_config.textproto"
+                value="/data/misc/perfetto-traces/trace_config.textproto"
+        />
+        <!--Install the content provider automatically when we push some file in sdcard folder.-->
+        <!--Needed to avoid the installation during the test suite.-->
+        <option name="push-file" key="trace_config.textproto" value="/sdcard/sample.textproto"/>
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="package" value="{PACKAGE}"/>
+        <option name="shell-timeout" value="6600s"/>
+        <option name="test-timeout" value="6000s"/>
+        <option name="hidden-api-checks" value="false"/>
+        <option name="device-listeners" value="android.device.collectors.PerfettoListener"/>
+        <!-- PerfettoListener related arguments -->
+        <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/>
+        <option name="instrumentation-arg"
+                key="perfetto_config_file"
+                value="trace_config.textproto"
+        />
+        <option name="instrumentation-arg" key="per_run" value="true"/>
+    </test>
+    <!-- Needed for pulling the collected trace config on to the host -->
+    <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+        <option name="pull-pattern-keys" value="perfetto_file_path"/>
+        <option name="directory-keys"
+                value="/data/user/0/com.android.wm.shell.flicker/files"/>
+        <option name="directory-keys"
+                value="/data/user/0/com.android.wm.shell.flicker.bubbles/files"/>
+        <option name="directory-keys"
+                value="/data/user/0/com.android.wm.shell.flicker.pip/files"/>
+        <option name="directory-keys"
+                value="/data/user/0/com.android.wm.shell.flicker.splitscreen/files"/>
+        <option name="directory-keys"
+                value="/data/user/0/com.android.wm.shell.flicker.service/files"/>
+        <option name="collect-on-run-ended-only" value="true"/>
+        <option name="clean-up" value="true"/>
+    </metrics_collector>
+</configuration>
diff --git a/libs/WindowManager/Shell/tests/flicker/res/xml/network_security_config.xml b/libs/WindowManager/Shell/tests/flicker/appcompat/res/xml/network_security_config.xml
similarity index 100%
copy from libs/WindowManager/Shell/tests/flicker/res/xml/network_security_config.xml
copy to libs/WindowManager/Shell/tests/flicker/appcompat/res/xml/network_security_config.xml
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt
rename to libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/LetterboxRule.kt b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/LetterboxRule.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/LetterboxRule.kt
rename to libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/LetterboxRule.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt
rename to libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenTransparentActivityTest.kt b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/OpenTransparentActivityTest.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenTransparentActivityTest.kt
rename to libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/OpenTransparentActivityTest.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt
rename to libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt
rename to libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt
rename to libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RotateImmersiveAppInFullscreenTest.kt b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RotateImmersiveAppInFullscreenTest.kt
similarity index 80%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RotateImmersiveAppInFullscreenTest.kt
rename to libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RotateImmersiveAppInFullscreenTest.kt
index ba2b3e7..446aad8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RotateImmersiveAppInFullscreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RotateImmersiveAppInFullscreenTest.kt
@@ -17,11 +17,11 @@
 package com.android.wm.shell.flicker.appcompat
 
 import android.os.Build
-import android.tools.common.datatypes.Rect
 import android.platform.test.annotations.Postsubmit
 import android.system.helpers.CommandsHelper
 import android.tools.common.NavBar
 import android.tools.common.Rotation
+import android.tools.common.datatypes.Rect
 import android.tools.common.flicker.assertions.FlickerTest
 import android.tools.common.traces.component.ComponentNameMatcher
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
@@ -65,10 +65,12 @@
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 class RotateImmersiveAppInFullscreenTest(flicker: LegacyFlickerTest) : BaseAppCompat(flicker) {
 
-    private val immersiveApp = LetterboxAppHelper(instrumentation,
+    private val immersiveApp =
+        LetterboxAppHelper(
+            instrumentation,
             launcherName = ActivityOptions.PortraitImmersiveActivity.LABEL,
-            component =
-            ActivityOptions.PortraitImmersiveActivity.COMPONENT.toFlickerComponent())
+            component = ActivityOptions.PortraitImmersiveActivity.COMPONENT.toFlickerComponent()
+        )
 
     private val cmdHelper: CommandsHelper = CommandsHelper.getInstance(instrumentation)
     private val execAdb: (String) -> String = { cmd -> cmdHelper.executeShellCommand(cmd) }
@@ -84,8 +86,8 @@
                 setStartRotation()
                 immersiveApp.launchViaIntent(wmHelper)
                 startDisplayBounds =
-                        wmHelper.currentState.layerState.physicalDisplayBounds
-                                ?: error("Display not found")
+                    wmHelper.currentState.layerState.physicalDisplayBounds
+                        ?: error("Display not found")
             }
             transitions {
                 if (isCuttlefishDevice) {
@@ -96,12 +98,10 @@
                     val rotationButtonSelector = By.res(LAUNCHER_PACKAGE, "rotate_suggestion")
                     uiDevice.wait(Until.hasObject(rotationButtonSelector), FIND_TIMEOUT)
                     uiDevice.findObject(rotationButtonSelector)
-                            ?: error("rotation button not found")
+                        ?: error("rotation button not found")
                 }
             }
-            teardown {
-                immersiveApp.exit(wmHelper)
-            }
+            teardown { immersiveApp.exit(wmHelper) }
         }
 
     @Before
@@ -112,48 +112,40 @@
     /** {@inheritDoc} */
     @Test
     @Ignore("Not applicable to this CUJ. App is in immersive mode.")
-    override fun taskBarLayerIsVisibleAtStartAndEnd() {
-    }
+    override fun taskBarLayerIsVisibleAtStartAndEnd() {}
 
     /** {@inheritDoc} */
     @Test
     @Ignore("Not applicable to this CUJ. App is in immersive mode.")
-    override fun navBarLayerIsVisibleAtStartAndEnd() {
-    }
+    override fun navBarLayerIsVisibleAtStartAndEnd() {}
 
     /** {@inheritDoc} */
     @Test
     @Ignore("Not applicable to this CUJ. App is in immersive mode.")
-    override fun statusBarLayerIsVisibleAtStartAndEnd() {
-    }
+    override fun statusBarLayerIsVisibleAtStartAndEnd() {}
 
     /** {@inheritDoc} */
     @Test
     @Ignore("Not applicable to this CUJ. App is in immersive mode.")
-    override fun taskBarWindowIsAlwaysVisible() {
-    }
+    override fun taskBarWindowIsAlwaysVisible() {}
 
     /** {@inheritDoc} */
     @Test
     @Ignore("Not applicable to this CUJ. App is in immersive mode.")
-    override fun navBarWindowIsAlwaysVisible() {
-    }
+    override fun navBarWindowIsAlwaysVisible() {}
 
     /** {@inheritDoc} */
     @Test
     @Ignore("Not applicable to this CUJ. App is in immersive mode.")
-    override fun statusBarWindowIsAlwaysVisible() {
-    }
+    override fun statusBarWindowIsAlwaysVisible() {}
 
     @Test
     @Ignore("Not applicable to this CUJ. App is in immersive mode.")
-    override fun statusBarLayerPositionAtStartAndEnd() {
-    }
+    override fun statusBarLayerPositionAtStartAndEnd() {}
 
     @Test
     @Ignore("Not applicable to this CUJ. App is in immersive mode.")
-    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
-    }
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() {}
 
     /** Test that app is fullscreen by checking status bar and task bar visibility. */
     @Postsubmit
@@ -161,8 +153,9 @@
     fun appWindowFullScreen() {
         flicker.assertWmEnd {
             this.isAppWindowInvisible(ComponentNameMatcher.STATUS_BAR)
-                    .isAppWindowInvisible(ComponentNameMatcher.TASK_BAR)
-                    .visibleRegion(immersiveApp).coversExactly(startDisplayBounds)
+                .isAppWindowInvisible(ComponentNameMatcher.TASK_BAR)
+                .visibleRegion(immersiveApp)
+                .coversExactly(startDisplayBounds)
         }
     }
 
@@ -170,9 +163,7 @@
     @Postsubmit
     @Test
     fun appInOriginalRotation() {
-        flicker.assertWmEnd {
-            this.hasRotation(Rotation.ROTATION_90)
-        }
+        flicker.assertWmEnd { this.hasRotation(Rotation.ROTATION_90) }
     }
 
     companion object {
@@ -189,10 +180,10 @@
         @JvmStatic
         fun getParams(): Collection<FlickerTest> {
             return LegacyFlickerTestFactory.nonRotationTests(
-                    supportedRotations = listOf(Rotation.ROTATION_90),
-                    // TODO(b/292403378): 3 button mode not added as rotation button is hidden in taskbar
-                    supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
-
+                supportedRotations = listOf(Rotation.ROTATION_90),
+                // TODO(b/292403378): 3 button mode not added as rotation button is hidden in
+                // taskbar
+                supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
             )
         }
     }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/TransparentBaseAppCompat.kt b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/TransparentBaseAppCompat.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/TransparentBaseAppCompat.kt
rename to libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/TransparentBaseAppCompat.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/trace_config/trace_config.textproto b/libs/WindowManager/Shell/tests/flicker/appcompat/trace_config/trace_config.textproto
new file mode 100644
index 0000000..406ada9
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/appcompat/trace_config/trace_config.textproto
@@ -0,0 +1,75 @@
+# 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.
+
+# proto-message: TraceConfig
+
+# Enable periodic flushing of the trace buffer into the output file.
+write_into_file: true
+
+# Writes the userspace buffer into the file every 1s.
+file_write_period_ms: 2500
+
+# See b/126487238 - we need to guarantee ordering of events.
+flush_period_ms: 30000
+
+# The trace buffers needs to be big enough to hold |file_write_period_ms| of
+# trace data. The trace buffer sizing depends on the number of trace categories
+# enabled and the device activity.
+
+# RSS events
+buffers: {
+  size_kb: 63488
+  fill_policy: RING_BUFFER
+}
+
+data_sources {
+  config {
+    name: "linux.process_stats"
+    target_buffer: 0
+    # polled per-process memory counters and process/thread names.
+    # If you don't want the polled counters, remove the "process_stats_config"
+    # section, but keep the data source itself as it still provides on-demand
+    # thread/process naming for ftrace data below.
+    process_stats_config {
+      scan_all_processes_on_start: true
+    }
+  }
+}
+
+data_sources: {
+  config {
+    name: "linux.ftrace"
+    ftrace_config {
+      ftrace_events: "ftrace/print"
+      ftrace_events: "task/task_newtask"
+      ftrace_events: "task/task_rename"
+      atrace_categories: "ss"
+      atrace_categories: "wm"
+      atrace_categories: "am"
+      atrace_categories: "aidl"
+      atrace_categories: "input"
+      atrace_categories: "binder_driver"
+      atrace_categories: "sched_process_exit"
+      atrace_apps: "com.android.server.wm.flicker.testapp"
+      atrace_apps: "com.android.systemui"
+      atrace_apps: "com.android.wm.shell.flicker"
+      atrace_apps: "com.android.wm.shell.flicker.other"
+      atrace_apps: "com.android.wm.shell.flicker.bubbles"
+      atrace_apps: "com.android.wm.shell.flicker.pip"
+      atrace_apps: "com.android.wm.shell.flicker.splitscreen"
+      atrace_apps: "com.google.android.apps.nexuslauncher"
+    }
+  }
+}
+
diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/Android.bp b/libs/WindowManager/Shell/tests/flicker/bubble/Android.bp
new file mode 100644
index 0000000..c4e9a84
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/bubble/Android.bp
@@ -0,0 +1,34 @@
+//
+// Copyright (C) 2020 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 {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+    name: "WMShellFlickerTestsBubbles",
+    defaults: ["WMShellFlickerTestsDefault"],
+    manifest: "AndroidManifest.xml",
+    package_name: "com.android.wm.shell.flicker.bubbles",
+    instrumentation_target_package: "com.android.wm.shell.flicker.bubbles",
+    srcs: ["src/**/*.kt"],
+    static_libs: ["WMShellFlickerTestsBase"],
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/bubble/AndroidManifest.xml
similarity index 76%
copy from libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml
copy to libs/WindowManager/Shell/tests/flicker/bubble/AndroidManifest.xml
index ae130b8..e6e6f1b 100644
--- a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/bubble/AndroidManifest.xml
@@ -1,22 +1,22 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 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) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           xmlns:tools="http://schemas.android.com/tools"
-          package="com.android.wm.shell.flicker">
+          package="com.android.wm.shell.flicker.bubble">
 
     <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/>
     <!-- Read and write traces from external storage -->
@@ -69,4 +69,9 @@
             android:authorities="${applicationId}.androidx-startup"
             tools:node="remove" />
     </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.wm.shell.flicker.bubble"
+                     android:label="WindowManager Flicker Tests">
+    </instrumentation>
 </manifest>
diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml
new file mode 100644
index 0000000..1df1136
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<configuration description="Runs WindowManager Shell Flicker Tests {MODULE}">
+    <option name="test-tag" value="FlickerTests"/>
+    <!-- Needed for storing the perfetto trace files in the sdcard/test_results-->
+    <option name="isolated-storage" value="false"/>
+
+    <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+        <!-- keeps the screen on during tests -->
+        <option name="screen-always-on" value="on"/>
+        <!-- prevents the phone from restarting -->
+        <option name="force-skip-system-props" value="true"/>
+        <!-- set WM tracing verbose level to all -->
+        <option name="run-command" value="cmd window tracing level all"/>
+        <!-- set WM tracing to frame (avoid incomplete states) -->
+        <option name="run-command" value="cmd window tracing frame"/>
+        <!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests -->
+        <option name="run-command" value="pm disable com.google.android.internal.betterbug"/>
+        <!-- ensure lock screen mode is swipe -->
+        <option name="run-command" value="locksettings set-disabled false"/>
+        <!-- restart launcher to activate TAPL -->
+        <option name="run-command"
+                value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher"/>
+        <!-- Increase trace size: 20mb for WM and 80mb for SF -->
+        <option name="run-command" value="cmd window tracing size 20480"/>
+        <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/>
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <option name="test-user-token" value="%TEST_USER%"/>
+        <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
+        <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
+        <option name="run-command" value="settings put system show_touches 1"/>
+        <option name="run-command" value="settings put system pointer_location 1"/>
+        <option name="teardown-command"
+                value="settings delete secure show_ime_with_hard_keyboard"/>
+        <option name="teardown-command" value="settings delete system show_touches"/>
+        <option name="teardown-command" value="settings delete system pointer_location"/>
+        <option name="teardown-command"
+                value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/>
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true"/>
+        <option name="test-file-name" value="{MODULE}.apk"/>
+        <option name="test-file-name" value="FlickerTestApp.apk"/>
+    </target_preparer>
+    <!-- Enable mocking GPS location by the test app -->
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <option name="run-command"
+                value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location allow"/>
+        <option name="teardown-command"
+                value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location deny"/>
+    </target_preparer>
+
+    <!-- Needed for pushing the trace config file -->
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="push-file"
+                key="trace_config.textproto"
+                value="/data/misc/perfetto-traces/trace_config.textproto"
+        />
+        <!--Install the content provider automatically when we push some file in sdcard folder.-->
+        <!--Needed to avoid the installation during the test suite.-->
+        <option name="push-file" key="trace_config.textproto" value="/sdcard/sample.textproto"/>
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="package" value="{PACKAGE}"/>
+        <option name="shell-timeout" value="6600s"/>
+        <option name="test-timeout" value="6000s"/>
+        <option name="hidden-api-checks" value="false"/>
+        <option name="device-listeners" value="android.device.collectors.PerfettoListener"/>
+        <!-- PerfettoListener related arguments -->
+        <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/>
+        <option name="instrumentation-arg"
+                key="perfetto_config_file"
+                value="trace_config.textproto"
+        />
+        <option name="instrumentation-arg" key="per_run" value="true"/>
+    </test>
+    <!-- Needed for pulling the collected trace config on to the host -->
+    <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+        <option name="pull-pattern-keys" value="perfetto_file_path"/>
+        <option name="directory-keys"
+                value="/data/user/0/com.android.wm.shell.flicker/files"/>
+        <option name="directory-keys"
+                value="/data/user/0/com.android.wm.shell.flicker.bubbles/files"/>
+        <option name="directory-keys"
+                value="/data/user/0/com.android.wm.shell.flicker.pip/files"/>
+        <option name="directory-keys"
+                value="/data/user/0/com.android.wm.shell.flicker.splitscreen/files"/>
+        <option name="directory-keys"
+                value="/data/user/0/com.android.wm.shell.flicker.service/files"/>
+        <option name="collect-on-run-ended-only" value="true"/>
+        <option name="clean-up" value="true"/>
+    </metrics_collector>
+</configuration>
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OWNERS b/libs/WindowManager/Shell/tests/flicker/bubble/OWNERS
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OWNERS
rename to libs/WindowManager/Shell/tests/flicker/bubble/OWNERS
diff --git a/libs/WindowManager/Shell/tests/flicker/res/xml/network_security_config.xml b/libs/WindowManager/Shell/tests/flicker/bubble/res/xml/network_security_config.xml
similarity index 100%
copy from libs/WindowManager/Shell/tests/flicker/res/xml/network_security_config.xml
copy to libs/WindowManager/Shell/tests/flicker/bubble/res/xml/network_security_config.xml
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
similarity index 93%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
rename to libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
index bc095bb..0c36e29 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
@@ -58,8 +58,9 @@
         return {
             setup {
                 MultiWindowUtils.executeShellCommand(
-                        instrumentation,
-                        "settings put secure force_hide_bubbles_user_education 1")
+                    instrumentation,
+                    "settings put secure force_hide_bubbles_user_education 1"
+                )
                 notifyManager.setBubblesAllowed(
                     testApp.packageName,
                     uid,
@@ -72,8 +73,9 @@
 
             teardown {
                 MultiWindowUtils.executeShellCommand(
-                        instrumentation,
-                        "settings put secure force_hide_bubbles_user_education 0")
+                    instrumentation,
+                    "settings put secure force_hide_bubbles_user_education 0"
+                )
                 notifyManager.setBubblesAllowed(
                     testApp.packageName,
                     uid,
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt
rename to libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt
rename to libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt
rename to libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt
rename to libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt
rename to libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/trace_config/trace_config.textproto b/libs/WindowManager/Shell/tests/flicker/bubble/trace_config/trace_config.textproto
new file mode 100644
index 0000000..406ada9
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/bubble/trace_config/trace_config.textproto
@@ -0,0 +1,75 @@
+# 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.
+
+# proto-message: TraceConfig
+
+# Enable periodic flushing of the trace buffer into the output file.
+write_into_file: true
+
+# Writes the userspace buffer into the file every 1s.
+file_write_period_ms: 2500
+
+# See b/126487238 - we need to guarantee ordering of events.
+flush_period_ms: 30000
+
+# The trace buffers needs to be big enough to hold |file_write_period_ms| of
+# trace data. The trace buffer sizing depends on the number of trace categories
+# enabled and the device activity.
+
+# RSS events
+buffers: {
+  size_kb: 63488
+  fill_policy: RING_BUFFER
+}
+
+data_sources {
+  config {
+    name: "linux.process_stats"
+    target_buffer: 0
+    # polled per-process memory counters and process/thread names.
+    # If you don't want the polled counters, remove the "process_stats_config"
+    # section, but keep the data source itself as it still provides on-demand
+    # thread/process naming for ftrace data below.
+    process_stats_config {
+      scan_all_processes_on_start: true
+    }
+  }
+}
+
+data_sources: {
+  config {
+    name: "linux.ftrace"
+    ftrace_config {
+      ftrace_events: "ftrace/print"
+      ftrace_events: "task/task_newtask"
+      ftrace_events: "task/task_rename"
+      atrace_categories: "ss"
+      atrace_categories: "wm"
+      atrace_categories: "am"
+      atrace_categories: "aidl"
+      atrace_categories: "input"
+      atrace_categories: "binder_driver"
+      atrace_categories: "sched_process_exit"
+      atrace_apps: "com.android.server.wm.flicker.testapp"
+      atrace_apps: "com.android.systemui"
+      atrace_apps: "com.android.wm.shell.flicker"
+      atrace_apps: "com.android.wm.shell.flicker.other"
+      atrace_apps: "com.android.wm.shell.flicker.bubbles"
+      atrace_apps: "com.android.wm.shell.flicker.pip"
+      atrace_apps: "com.android.wm.shell.flicker.splitscreen"
+      atrace_apps: "com.google.android.apps.nexuslauncher"
+    }
+  }
+}
+
diff --git a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestBubbles.xml b/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestBubbles.xml
deleted file mode 100644
index 437871f..0000000
--- a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestBubbles.xml
+++ /dev/null
@@ -1,24 +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.
-  -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.wm.shell.flicker.bubble">
-
-    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
-                     android:targetPackage="com.android.wm.shell.flicker.bubble"
-                     android:label="WindowManager Flicker Tests">
-    </instrumentation>
-</manifest>
diff --git a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestOther.xml b/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestOther.xml
deleted file mode 100644
index cf642f6..0000000
--- a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestOther.xml
+++ /dev/null
@@ -1,24 +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.
-  -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.wm.shell.flicker">
-
-    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
-                     android:targetPackage="com.android.wm.shell.flicker"
-                     android:label="WindowManager Flicker Tests">
-    </instrumentation>
-</manifest>
diff --git a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestPip.xml b/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestPip.xml
deleted file mode 100644
index fa42a45..0000000
--- a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestPip.xml
+++ /dev/null
@@ -1,27 +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.
-  -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.wm.shell.flicker.pip">
-
-    <!-- Enable mocking GPS location -->
-    <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/>
-
-    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
-                     android:targetPackage="com.android.wm.shell.flicker.pip"
-                     android:label="WindowManager Flicker Tests">
-    </instrumentation>
-</manifest>
diff --git a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestService.xml b/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestService.xml
deleted file mode 100644
index c7aca1a..0000000
--- a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestService.xml
+++ /dev/null
@@ -1,24 +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.
-  -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.wm.shell.flicker.service">
-
-    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
-                     android:targetPackage="com.android.wm.shell.flicker.service"
-                     android:label="WindowManager Flicker Service Tests">
-    </instrumentation>
-</manifest>
diff --git a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestSplitScreen.xml b/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestSplitScreen.xml
deleted file mode 100644
index 887d8db..0000000
--- a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestSplitScreen.xml
+++ /dev/null
@@ -1,24 +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.
-  -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.wm.shell.flicker.splitscreen">
-
-    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
-                     android:targetPackage="com.android.wm.shell.flicker.splitscreen"
-                     android:label="WindowManager Flicker Tests">
-    </instrumentation>
-</manifest>
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/Android.bp b/libs/WindowManager/Shell/tests/flicker/pip/Android.bp
new file mode 100644
index 0000000..386983c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/pip/Android.bp
@@ -0,0 +1,136 @@
+//
+// Copyright (C) 2020 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 {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+filegroup {
+    name: "WMShellFlickerTestsPip1-src",
+    srcs: [
+        "src/**/A*.kt",
+        "src/**/B*.kt",
+        "src/**/C*.kt",
+        "src/**/D*.kt",
+        "src/**/S*.kt",
+    ],
+}
+
+filegroup {
+    name: "WMShellFlickerTestsPip2-src",
+    srcs: [
+        "src/**/E*.kt",
+    ],
+}
+
+filegroup {
+    name: "WMShellFlickerTestsPip3-src",
+    srcs: ["src/**/*.kt"],
+}
+
+filegroup {
+    name: "WMShellFlickerTestsPipCommon-src",
+    srcs: ["src/**/common/*.kt"],
+}
+
+filegroup {
+    name: "WMShellFlickerTestsPipApps-src",
+    srcs: ["src/**/apps/*.kt"],
+}
+
+android_test {
+    name: "WMShellFlickerTestsPip1",
+    defaults: ["WMShellFlickerTestsDefault"],
+    manifest: "AndroidManifest.xml",
+    package_name: "com.android.wm.shell.flicker.pip",
+    instrumentation_target_package: "com.android.wm.shell.flicker.pip",
+    srcs: [
+        ":WMShellFlickerTestsPip1-src",
+        ":WMShellFlickerTestsPipCommon-src",
+    ],
+    static_libs: ["WMShellFlickerTestsBase"],
+}
+
+android_test {
+    name: "WMShellFlickerTestsPip2",
+    defaults: ["WMShellFlickerTestsDefault"],
+    manifest: "AndroidManifest.xml",
+    package_name: "com.android.wm.shell.flicker.pip",
+    instrumentation_target_package: "com.android.wm.shell.flicker.pip",
+    srcs: [
+        ":WMShellFlickerTestsPip2-src",
+        ":WMShellFlickerTestsPipCommon-src",
+    ],
+    static_libs: ["WMShellFlickerTestsBase"],
+}
+
+android_test {
+    name: "WMShellFlickerTestsPip3",
+    defaults: ["WMShellFlickerTestsDefault"],
+    manifest: "AndroidManifest.xml",
+    package_name: "com.android.wm.shell.flicker.pip",
+    instrumentation_target_package: "com.android.wm.shell.flicker.pip",
+    srcs: [
+        ":WMShellFlickerTestsPip3-src",
+        ":WMShellFlickerTestsPipCommon-src",
+    ],
+    exclude_srcs: [
+        ":WMShellFlickerTestsPip1-src",
+        ":WMShellFlickerTestsPip2-src",
+        ":WMShellFlickerTestsPipApps-src",
+    ],
+    static_libs: ["WMShellFlickerTestsBase"],
+}
+
+android_test {
+    name: "WMShellFlickerTestsPipApps",
+    defaults: ["WMShellFlickerTestsDefault"],
+    manifest: "AndroidManifest.xml",
+    package_name: "com.android.wm.shell.flicker.pip.apps",
+    instrumentation_target_package: "com.android.wm.shell.flicker.pip.apps",
+    srcs: [
+        ":WMShellFlickerTestsPipApps-src",
+        ":WMShellFlickerTestsPipCommon-src",
+    ],
+    static_libs: ["WMShellFlickerTestsBase"],
+}
+
+android_test {
+    name: "WMShellFlickerTestsPipAppsCSuite",
+    defaults: ["WMShellFlickerTestsDefaultWithoutTemplate"],
+    additional_manifests: ["AndroidManifest.xml"],
+    package_name: "com.android.wm.shell.flicker.pip.apps",
+    instrumentation_target_package: "com.android.wm.shell.flicker.pip.apps",
+    srcs: [
+        ":WMShellFlickerTestsPipApps-src",
+        ":WMShellFlickerTestsPipCommon-src",
+    ],
+    static_libs: ["WMShellFlickerTestsBase"],
+    test_suites: [
+        "device-tests",
+        "csuite",
+    ],
+}
+
+csuite_test {
+    name: "csuite-1p3p-pip-flickers",
+    test_config_template: "csuiteDefaultTemplate.xml",
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/pip/AndroidManifest.xml
similarity index 74%
copy from libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml
copy to libs/WindowManager/Shell/tests/flicker/pip/AndroidManifest.xml
index ae130b8..6d5423b 100644
--- a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/pip/AndroidManifest.xml
@@ -1,22 +1,22 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 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) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           xmlns:tools="http://schemas.android.com/tools"
-          package="com.android.wm.shell.flicker">
+          package="com.android.wm.shell.flicker.pip">
 
     <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/>
     <!-- Read and write traces from external storage -->
@@ -69,4 +69,12 @@
             android:authorities="${applicationId}.androidx-startup"
             tools:node="remove" />
     </application>
+
+    <!-- Enable mocking GPS location -->
+    <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.wm.shell.flicker.pip"
+                     android:label="WindowManager Flicker Tests">
+    </instrumentation>
 </manifest>
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml
new file mode 100644
index 0000000..1df1136
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<configuration description="Runs WindowManager Shell Flicker Tests {MODULE}">
+    <option name="test-tag" value="FlickerTests"/>
+    <!-- Needed for storing the perfetto trace files in the sdcard/test_results-->
+    <option name="isolated-storage" value="false"/>
+
+    <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+        <!-- keeps the screen on during tests -->
+        <option name="screen-always-on" value="on"/>
+        <!-- prevents the phone from restarting -->
+        <option name="force-skip-system-props" value="true"/>
+        <!-- set WM tracing verbose level to all -->
+        <option name="run-command" value="cmd window tracing level all"/>
+        <!-- set WM tracing to frame (avoid incomplete states) -->
+        <option name="run-command" value="cmd window tracing frame"/>
+        <!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests -->
+        <option name="run-command" value="pm disable com.google.android.internal.betterbug"/>
+        <!-- ensure lock screen mode is swipe -->
+        <option name="run-command" value="locksettings set-disabled false"/>
+        <!-- restart launcher to activate TAPL -->
+        <option name="run-command"
+                value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher"/>
+        <!-- Increase trace size: 20mb for WM and 80mb for SF -->
+        <option name="run-command" value="cmd window tracing size 20480"/>
+        <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/>
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <option name="test-user-token" value="%TEST_USER%"/>
+        <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
+        <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
+        <option name="run-command" value="settings put system show_touches 1"/>
+        <option name="run-command" value="settings put system pointer_location 1"/>
+        <option name="teardown-command"
+                value="settings delete secure show_ime_with_hard_keyboard"/>
+        <option name="teardown-command" value="settings delete system show_touches"/>
+        <option name="teardown-command" value="settings delete system pointer_location"/>
+        <option name="teardown-command"
+                value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/>
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true"/>
+        <option name="test-file-name" value="{MODULE}.apk"/>
+        <option name="test-file-name" value="FlickerTestApp.apk"/>
+    </target_preparer>
+    <!-- Enable mocking GPS location by the test app -->
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <option name="run-command"
+                value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location allow"/>
+        <option name="teardown-command"
+                value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location deny"/>
+    </target_preparer>
+
+    <!-- Needed for pushing the trace config file -->
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="push-file"
+                key="trace_config.textproto"
+                value="/data/misc/perfetto-traces/trace_config.textproto"
+        />
+        <!--Install the content provider automatically when we push some file in sdcard folder.-->
+        <!--Needed to avoid the installation during the test suite.-->
+        <option name="push-file" key="trace_config.textproto" value="/sdcard/sample.textproto"/>
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="package" value="{PACKAGE}"/>
+        <option name="shell-timeout" value="6600s"/>
+        <option name="test-timeout" value="6000s"/>
+        <option name="hidden-api-checks" value="false"/>
+        <option name="device-listeners" value="android.device.collectors.PerfettoListener"/>
+        <!-- PerfettoListener related arguments -->
+        <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/>
+        <option name="instrumentation-arg"
+                key="perfetto_config_file"
+                value="trace_config.textproto"
+        />
+        <option name="instrumentation-arg" key="per_run" value="true"/>
+    </test>
+    <!-- Needed for pulling the collected trace config on to the host -->
+    <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+        <option name="pull-pattern-keys" value="perfetto_file_path"/>
+        <option name="directory-keys"
+                value="/data/user/0/com.android.wm.shell.flicker/files"/>
+        <option name="directory-keys"
+                value="/data/user/0/com.android.wm.shell.flicker.bubbles/files"/>
+        <option name="directory-keys"
+                value="/data/user/0/com.android.wm.shell.flicker.pip/files"/>
+        <option name="directory-keys"
+                value="/data/user/0/com.android.wm.shell.flicker.splitscreen/files"/>
+        <option name="directory-keys"
+                value="/data/user/0/com.android.wm.shell.flicker.service/files"/>
+        <option name="collect-on-run-ended-only" value="true"/>
+        <option name="clean-up" value="true"/>
+    </metrics_collector>
+</configuration>
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/OWNERS b/libs/WindowManager/Shell/tests/flicker/pip/OWNERS
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/OWNERS
rename to libs/WindowManager/Shell/tests/flicker/pip/OWNERS
diff --git a/libs/WindowManager/Shell/tests/flicker/csuiteDefaultTemplate.xml b/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/csuiteDefaultTemplate.xml
rename to libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml
diff --git a/libs/WindowManager/Shell/tests/flicker/res/xml/network_security_config.xml b/libs/WindowManager/Shell/tests/flicker/pip/res/xml/network_security_config.xml
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/res/xml/network_security_config.xml
rename to libs/WindowManager/Shell/tests/flicker/pip/res/xml/network_security_config.xml
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt
similarity index 95%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt
index 943b16c..a5c2c89 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt
@@ -81,8 +81,13 @@
                 pipApp.launchViaIntent(wmHelper)
                 tapl.goHome()
                 SplitScreenUtils.enterSplit(
-                    wmHelper, tapl, device, pipApp, secondAppForSplitScreen,
-                    flicker.scenario.startRotation)
+                    wmHelper,
+                    tapl,
+                    device,
+                    pipApp,
+                    secondAppForSplitScreen,
+                    flicker.scenario.startRotation
+                )
                 pipApp.enableAutoEnterForPipActivity()
             }
             teardown {
@@ -132,9 +137,7 @@
             if (flicker.scenario.isLandscapeOrSeascapeAtStart) {
                 flicker.assertWmVisibleRegion(pipApp) {
                     // first check against landscape bounds then against portrait bounds
-                    coversAtMost(displayBounds).then().coversAtMost(
-                        portraitDisplayBounds
-                    )
+                    coversAtMost(displayBounds).then().coversAtMost(portraitDisplayBounds)
                 }
             } else {
                 // always check against the display bounds which do not change during transition
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
similarity index 95%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
index 94e3959..af2db12 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
@@ -55,8 +55,7 @@
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class AutoEnterPipOnGoToHomeTest(flicker: LegacyFlickerTest) :
-    EnterPipTransition(flicker) {
+open class AutoEnterPipOnGoToHomeTest(flicker: LegacyFlickerTest) : EnterPipTransition(flicker) {
     override val thisTransition: FlickerBuilder.() -> Unit = { transitions { tapl.goHome() } }
 
     override val defaultEnterPip: FlickerBuilder.() -> Unit = {
@@ -66,11 +65,7 @@
         }
     }
 
-    override val defaultTeardown: FlickerBuilder.() -> Unit = {
-        teardown {
-            pipApp.exit(wmHelper)
-        }
-    }
+    override val defaultTeardown: FlickerBuilder.() -> Unit = { teardown { pipApp.exit(wmHelper) } }
 
     @FlakyTest(bugId = 293133362)
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt
similarity index 97%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt
index 820af93..4cc9547 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt
@@ -53,11 +53,7 @@
         }
     }
 
-    override val defaultTeardown: FlickerBuilder.() -> Unit = {
-        teardown {
-            pipApp.exit(wmHelper)
-        }
-    }
+    override val defaultTeardown: FlickerBuilder.() -> Unit = { teardown { pipApp.exit(wmHelper) } }
 
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt
similarity index 96%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt
index 4f27ced..36047cc 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt
@@ -35,9 +35,7 @@
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 class PipAspectRatioChangeTest(flicker: LegacyFlickerTest) : PipTransition(flicker) {
     override val thisTransition: FlickerBuilder.() -> Unit = {
-        transitions {
-            pipApp.changeAspectRatio()
-        }
+        transitions { pipApp.changeAspectRatio() }
     }
 
     @Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragTest.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragTest.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt
similarity index 98%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt
index 0fd1b2c..381f947 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt
@@ -42,8 +42,8 @@
     }
 
     /**
-     * Checks that the visible region area of [pipApp] decreases
-     * and then increases during the animation.
+     * Checks that the visible region area of [pipApp] decreases and then increases during the
+     * animation.
      */
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt
similarity index 95%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt
index c9a98c7..be5a27a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt
@@ -81,9 +81,8 @@
     @Test
     override fun pipLayerReduces() {
         flicker.assertLayers {
-            val pipLayerList = this.layers {
-                standardAppHelper.layerMatchesAnyOf(it) && it.isVisible
-            }
+            val pipLayerList =
+                this.layers { standardAppHelper.layerMatchesAnyOf(it) && it.isVisible }
             pipLayerList.zipWithNext { previous, current ->
                 current.visibleRegion.notBiggerThan(previous.visibleRegion.region)
             }
@@ -194,7 +193,8 @@
      * transition
      */
     @Postsubmit
-    @Test override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
+    @Test
+    override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
 
     /**
      * Checks that all layers that are visible on the trace, are visible for at least 2 consecutive
@@ -215,9 +215,7 @@
         super.visibleWindowsShownMoreThanOneConsecutiveEntry()
 
     /** Checks that all parts of the screen are covered during the transition */
-    @Postsubmit
-    @Test
-    override fun entireScreenCovered() = super.entireScreenCovered()
+    @Postsubmit @Test override fun entireScreenCovered() = super.entireScreenCovered()
 
     companion object {
         /**
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt
index d7ba3d5..4da52ef 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt
@@ -69,20 +69,21 @@
     val mainHandler = Handler(Looper.getMainLooper())
     var mockLocationEnabled = false
 
-    val updateLocation = object : Runnable {
-        override fun run() {
-            // early bail out if mocking location is not enabled
-            if (!mockLocationEnabled) return
-            val location = Location("Googleplex")
-            location.latitude = 37.42243438411294
-            location.longitude = -122.08426281892311
-            location.time = System.currentTimeMillis()
-            location.elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos()
-            location.accuracy = 100f
-            locationManager.setTestProviderLocation(LocationManager.GPS_PROVIDER, location)
-            mainHandler.postDelayed(this, 5)
+    val updateLocation =
+        object : Runnable {
+            override fun run() {
+                // early bail out if mocking location is not enabled
+                if (!mockLocationEnabled) return
+                val location = Location("Googleplex")
+                location.latitude = 37.42243438411294
+                location.longitude = -122.08426281892311
+                location.time = System.currentTimeMillis()
+                location.elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos()
+                location.accuracy = 100f
+                locationManager.setTestProviderLocation(LocationManager.GPS_PROVIDER, location)
+                mainHandler.postDelayed(this, 5)
+            }
         }
-    }
 
     override val defaultEnterPip: FlickerBuilder.() -> Unit = {
         setup {
@@ -129,9 +130,7 @@
         }
     }
 
-    override val thisTransition: FlickerBuilder.() -> Unit = {
-        transitions { tapl.goHome() }
-    }
+    override val thisTransition: FlickerBuilder.() -> Unit = { transitions { tapl.goHome() } }
 
     @Postsubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt
similarity index 93%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt
index 5965805..5498e8c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt
@@ -67,25 +67,18 @@
             standardAppHelper.launchViaIntent(
                 wmHelper,
                 NetflixAppHelper.getNetflixWatchVideoIntent("70184207"),
-                ComponentNameMatcher(
-                    NetflixAppHelper.PACKAGE_NAME,
-                    NetflixAppHelper.WATCH_ACTIVITY
-                )
+                ComponentNameMatcher(NetflixAppHelper.PACKAGE_NAME, NetflixAppHelper.WATCH_ACTIVITY)
             )
             standardAppHelper.waitForVideoPlaying()
         }
     }
 
     override val defaultTeardown: FlickerBuilder.() -> Unit = {
-        teardown {
-            standardAppHelper.exit(wmHelper)
-        }
+        teardown { standardAppHelper.exit(wmHelper) }
     }
 
     override val thisTransition: FlickerBuilder.() -> Unit = {
-        transitions {
-            tapl.goHomeFromImmersiveFullscreenApp()
-        }
+        transitions { tapl.goHomeFromImmersiveFullscreenApp() }
     }
 
     @Postsubmit
@@ -133,7 +126,8 @@
     }
 
     @Postsubmit
-    @Test override fun statusBarWindowIsAlwaysVisible() {
+    @Test
+    override fun statusBarWindowIsAlwaysVisible() {
         // Netflix plays in immersive fullscreen mode, so taskbar will be gone at some point
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt
similarity index 94%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt
index c370d91..d8afc25 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt
@@ -70,14 +70,10 @@
     }
 
     override val defaultTeardown: FlickerBuilder.() -> Unit = {
-        teardown {
-            standardAppHelper.exit(wmHelper)
-        }
+        teardown { standardAppHelper.exit(wmHelper) }
     }
 
-    override val thisTransition: FlickerBuilder.() -> Unit = {
-        transitions { tapl.goHome() }
-    }
+    override val thisTransition: FlickerBuilder.() -> Unit = { transitions { tapl.goHome() } }
 
     @Postsubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/ClosePipTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ClosePipTransition.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/ClosePipTransition.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ClosePipTransition.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/EnterPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/EnterPipTransition.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/EnterPipTransition.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/EnterPipTransition.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/ExitPipToAppTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ExitPipToAppTransition.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/ExitPipToAppTransition.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ExitPipToAppTransition.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/MovePipShelfHeightTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/MovePipShelfHeightTransition.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/MovePipShelfHeightTransition.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/MovePipShelfHeightTransition.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipAppHelperTv.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/PipAppHelperTv.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipAppHelperTv.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/PipAppHelperTv.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipTestBase.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/PipTestBase.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipTestBase.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/PipTestBase.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipBasicTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvPipBasicTest.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipBasicTest.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvPipBasicTest.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipNotificationTests.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvPipNotificationTests.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipNotificationTests.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvPipNotificationTests.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt
rename to libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/trace_config/trace_config.textproto b/libs/WindowManager/Shell/tests/flicker/pip/trace_config/trace_config.textproto
new file mode 100644
index 0000000..406ada9
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/pip/trace_config/trace_config.textproto
@@ -0,0 +1,75 @@
+# 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.
+
+# proto-message: TraceConfig
+
+# Enable periodic flushing of the trace buffer into the output file.
+write_into_file: true
+
+# Writes the userspace buffer into the file every 1s.
+file_write_period_ms: 2500
+
+# See b/126487238 - we need to guarantee ordering of events.
+flush_period_ms: 30000
+
+# The trace buffers needs to be big enough to hold |file_write_period_ms| of
+# trace data. The trace buffer sizing depends on the number of trace categories
+# enabled and the device activity.
+
+# RSS events
+buffers: {
+  size_kb: 63488
+  fill_policy: RING_BUFFER
+}
+
+data_sources {
+  config {
+    name: "linux.process_stats"
+    target_buffer: 0
+    # polled per-process memory counters and process/thread names.
+    # If you don't want the polled counters, remove the "process_stats_config"
+    # section, but keep the data source itself as it still provides on-demand
+    # thread/process naming for ftrace data below.
+    process_stats_config {
+      scan_all_processes_on_start: true
+    }
+  }
+}
+
+data_sources: {
+  config {
+    name: "linux.ftrace"
+    ftrace_config {
+      ftrace_events: "ftrace/print"
+      ftrace_events: "task/task_newtask"
+      ftrace_events: "task/task_rename"
+      atrace_categories: "ss"
+      atrace_categories: "wm"
+      atrace_categories: "am"
+      atrace_categories: "aidl"
+      atrace_categories: "input"
+      atrace_categories: "binder_driver"
+      atrace_categories: "sched_process_exit"
+      atrace_apps: "com.android.server.wm.flicker.testapp"
+      atrace_apps: "com.android.systemui"
+      atrace_apps: "com.android.wm.shell.flicker"
+      atrace_apps: "com.android.wm.shell.flicker.other"
+      atrace_apps: "com.android.wm.shell.flicker.bubbles"
+      atrace_apps: "com.android.wm.shell.flicker.pip"
+      atrace_apps: "com.android.wm.shell.flicker.splitscreen"
+      atrace_apps: "com.google.android.apps.nexuslauncher"
+    }
+  }
+}
+
diff --git a/libs/WindowManager/Shell/tests/flicker/service/Android.bp b/libs/WindowManager/Shell/tests/flicker/service/Android.bp
new file mode 100644
index 0000000..9b8cd94
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/service/Android.bp
@@ -0,0 +1,67 @@
+//
+// Copyright (C) 2020 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 {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+filegroup {
+    name: "WMShellFlickerServicePlatinumTests-src",
+    srcs: [
+        "src/**/platinum/*.kt",
+        "src/**/scenarios/*.kt",
+        "src/**/common/*.kt",
+    ],
+}
+
+java_library {
+    name: "wm-shell-flicker-platinum-tests",
+    platform_apis: true,
+    optimize: {
+        enabled: false,
+    },
+    srcs: [
+        ":WMShellFlickerServicePlatinumTests-src",
+    ],
+    static_libs: [
+        "wm-shell-flicker-utils",
+    ],
+}
+
+android_test {
+    name: "WMShellFlickerServiceTests",
+    defaults: ["WMShellFlickerTestsDefault"],
+    manifest: "AndroidManifest.xml",
+    package_name: "com.android.wm.shell.flicker.service",
+    instrumentation_target_package: "com.android.wm.shell.flicker.service",
+    srcs: ["src/**/*.kt"],
+    static_libs: ["WMShellFlickerTestsBase"],
+}
+
+android_test {
+    name: "WMShellFlickerServicePlatinumTests",
+    defaults: ["WMShellFlickerTestsDefault"],
+    manifest: "AndroidManifest.xml",
+    package_name: "com.android.wm.shell.flicker.service",
+    instrumentation_target_package: "com.android.wm.shell.flicker.service",
+    srcs: [":WMShellFlickerServicePlatinumTests-src"],
+    static_libs: ["WMShellFlickerTestsBase"],
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/service/AndroidManifest.xml
similarity index 75%
copy from libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml
copy to libs/WindowManager/Shell/tests/flicker/service/AndroidManifest.xml
index ae130b8..d54b694 100644
--- a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/service/AndroidManifest.xml
@@ -1,22 +1,22 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 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) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           xmlns:tools="http://schemas.android.com/tools"
-          package="com.android.wm.shell.flicker">
+          package="com.android.wm.shell.flicker.service">
 
     <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/>
     <!-- Read and write traces from external storage -->
@@ -69,4 +69,9 @@
             android:authorities="${applicationId}.androidx-startup"
             tools:node="remove" />
     </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.wm.shell.flicker.service"
+                     android:label="WindowManager Flicker Service Tests">
+    </instrumentation>
 </manifest>
diff --git a/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml
new file mode 100644
index 0000000..1df1136
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<configuration description="Runs WindowManager Shell Flicker Tests {MODULE}">
+    <option name="test-tag" value="FlickerTests"/>
+    <!-- Needed for storing the perfetto trace files in the sdcard/test_results-->
+    <option name="isolated-storage" value="false"/>
+
+    <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+        <!-- keeps the screen on during tests -->
+        <option name="screen-always-on" value="on"/>
+        <!-- prevents the phone from restarting -->
+        <option name="force-skip-system-props" value="true"/>
+        <!-- set WM tracing verbose level to all -->
+        <option name="run-command" value="cmd window tracing level all"/>
+        <!-- set WM tracing to frame (avoid incomplete states) -->
+        <option name="run-command" value="cmd window tracing frame"/>
+        <!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests -->
+        <option name="run-command" value="pm disable com.google.android.internal.betterbug"/>
+        <!-- ensure lock screen mode is swipe -->
+        <option name="run-command" value="locksettings set-disabled false"/>
+        <!-- restart launcher to activate TAPL -->
+        <option name="run-command"
+                value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher"/>
+        <!-- Increase trace size: 20mb for WM and 80mb for SF -->
+        <option name="run-command" value="cmd window tracing size 20480"/>
+        <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/>
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <option name="test-user-token" value="%TEST_USER%"/>
+        <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
+        <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
+        <option name="run-command" value="settings put system show_touches 1"/>
+        <option name="run-command" value="settings put system pointer_location 1"/>
+        <option name="teardown-command"
+                value="settings delete secure show_ime_with_hard_keyboard"/>
+        <option name="teardown-command" value="settings delete system show_touches"/>
+        <option name="teardown-command" value="settings delete system pointer_location"/>
+        <option name="teardown-command"
+                value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/>
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true"/>
+        <option name="test-file-name" value="{MODULE}.apk"/>
+        <option name="test-file-name" value="FlickerTestApp.apk"/>
+    </target_preparer>
+    <!-- Enable mocking GPS location by the test app -->
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <option name="run-command"
+                value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location allow"/>
+        <option name="teardown-command"
+                value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location deny"/>
+    </target_preparer>
+
+    <!-- Needed for pushing the trace config file -->
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="push-file"
+                key="trace_config.textproto"
+                value="/data/misc/perfetto-traces/trace_config.textproto"
+        />
+        <!--Install the content provider automatically when we push some file in sdcard folder.-->
+        <!--Needed to avoid the installation during the test suite.-->
+        <option name="push-file" key="trace_config.textproto" value="/sdcard/sample.textproto"/>
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="package" value="{PACKAGE}"/>
+        <option name="shell-timeout" value="6600s"/>
+        <option name="test-timeout" value="6000s"/>
+        <option name="hidden-api-checks" value="false"/>
+        <option name="device-listeners" value="android.device.collectors.PerfettoListener"/>
+        <!-- PerfettoListener related arguments -->
+        <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/>
+        <option name="instrumentation-arg"
+                key="perfetto_config_file"
+                value="trace_config.textproto"
+        />
+        <option name="instrumentation-arg" key="per_run" value="true"/>
+    </test>
+    <!-- Needed for pulling the collected trace config on to the host -->
+    <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+        <option name="pull-pattern-keys" value="perfetto_file_path"/>
+        <option name="directory-keys"
+                value="/data/user/0/com.android.wm.shell.flicker/files"/>
+        <option name="directory-keys"
+                value="/data/user/0/com.android.wm.shell.flicker.bubbles/files"/>
+        <option name="directory-keys"
+                value="/data/user/0/com.android.wm.shell.flicker.pip/files"/>
+        <option name="directory-keys"
+                value="/data/user/0/com.android.wm.shell.flicker.splitscreen/files"/>
+        <option name="directory-keys"
+                value="/data/user/0/com.android.wm.shell.flicker.service/files"/>
+        <option name="collect-on-run-ended-only" value="true"/>
+        <option name="clean-up" value="true"/>
+    </metrics_collector>
+</configuration>
diff --git a/libs/WindowManager/Shell/tests/flicker/res/xml/network_security_config.xml b/libs/WindowManager/Shell/tests/flicker/service/res/xml/network_security_config.xml
similarity index 100%
copy from libs/WindowManager/Shell/tests/flicker/res/xml/network_security_config.xml
copy to libs/WindowManager/Shell/tests/flicker/service/res/xml/network_security_config.xml
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/common/Utils.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/common/Utils.kt
similarity index 95%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/common/Utils.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/common/Utils.kt
index 5f15785..4bd7954 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/common/Utils.kt
+++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/common/Utils.kt
@@ -36,9 +36,7 @@
     fun testSetupRule(navigationMode: NavBar, rotation: Rotation): RuleChain {
         return RuleChain.outerRule(ArtifactSaverRule())
             .around(UnlockScreenRule())
-            .around(
-                NavigationModeRule(navigationMode.value, false)
-            )
+            .around(NavigationModeRule(navigationMode.value, false))
             .around(
                 LaunchAppRule(MessagingAppHelper(instrumentation), clearCacheAfterParsing = false)
             )
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/OWNERS b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/OWNERS
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/OWNERS
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/OWNERS
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromTaskbar.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromTaskbar.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromTaskbar.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromTaskbar.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/UnlockKeyguardToSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/UnlockKeyguardToSplitScreen.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/UnlockKeyguardToSplitScreen.kt
rename to libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/UnlockKeyguardToSplitScreen.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/service/trace_config/trace_config.textproto b/libs/WindowManager/Shell/tests/flicker/service/trace_config/trace_config.textproto
new file mode 100644
index 0000000..406ada9
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/service/trace_config/trace_config.textproto
@@ -0,0 +1,75 @@
+# 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.
+
+# proto-message: TraceConfig
+
+# Enable periodic flushing of the trace buffer into the output file.
+write_into_file: true
+
+# Writes the userspace buffer into the file every 1s.
+file_write_period_ms: 2500
+
+# See b/126487238 - we need to guarantee ordering of events.
+flush_period_ms: 30000
+
+# The trace buffers needs to be big enough to hold |file_write_period_ms| of
+# trace data. The trace buffer sizing depends on the number of trace categories
+# enabled and the device activity.
+
+# RSS events
+buffers: {
+  size_kb: 63488
+  fill_policy: RING_BUFFER
+}
+
+data_sources {
+  config {
+    name: "linux.process_stats"
+    target_buffer: 0
+    # polled per-process memory counters and process/thread names.
+    # If you don't want the polled counters, remove the "process_stats_config"
+    # section, but keep the data source itself as it still provides on-demand
+    # thread/process naming for ftrace data below.
+    process_stats_config {
+      scan_all_processes_on_start: true
+    }
+  }
+}
+
+data_sources: {
+  config {
+    name: "linux.ftrace"
+    ftrace_config {
+      ftrace_events: "ftrace/print"
+      ftrace_events: "task/task_newtask"
+      ftrace_events: "task/task_rename"
+      atrace_categories: "ss"
+      atrace_categories: "wm"
+      atrace_categories: "am"
+      atrace_categories: "aidl"
+      atrace_categories: "input"
+      atrace_categories: "binder_driver"
+      atrace_categories: "sched_process_exit"
+      atrace_apps: "com.android.server.wm.flicker.testapp"
+      atrace_apps: "com.android.systemui"
+      atrace_apps: "com.android.wm.shell.flicker"
+      atrace_apps: "com.android.wm.shell.flicker.other"
+      atrace_apps: "com.android.wm.shell.flicker.bubbles"
+      atrace_apps: "com.android.wm.shell.flicker.pip"
+      atrace_apps: "com.android.wm.shell.flicker.splitscreen"
+      atrace_apps: "com.google.android.apps.nexuslauncher"
+    }
+  }
+}
+
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp b/libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp
new file mode 100644
index 0000000..4629c53
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp
@@ -0,0 +1,77 @@
+//
+// Copyright (C) 2020 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 {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+filegroup {
+    name: "WMShellFlickerTestsSplitScreenBase-src",
+    srcs: [
+        "src/**/benchmark/*.kt",
+    ],
+}
+
+filegroup {
+    name: "WMShellFlickerTestsSplitScreenGroup1-src",
+    srcs: [
+        "src/**/A*.kt",
+        "src/**/B*.kt",
+        "src/**/C*.kt",
+        "src/**/D*.kt",
+        "src/**/E*.kt",
+    ],
+}
+
+filegroup {
+    name: "WMShellFlickerTestsSplitScreenGroup2-src",
+    srcs: [
+        "src/**/*.kt",
+    ],
+}
+
+android_test {
+    name: "WMShellFlickerTestsSplitScreenGroup1",
+    defaults: ["WMShellFlickerTestsDefault"],
+    manifest: "AndroidManifest.xml",
+    package_name: "com.android.wm.shell.flicker.splitscreen",
+    instrumentation_target_package: "com.android.wm.shell.flicker.splitscreen",
+    srcs: [
+        ":WMShellFlickerTestsSplitScreenBase-src",
+        ":WMShellFlickerTestsSplitScreenGroup1-src",
+    ],
+    static_libs: ["WMShellFlickerTestsBase"],
+}
+
+android_test {
+    name: "WMShellFlickerTestsSplitScreenGroup2",
+    manifest: "AndroidManifest.xml",
+    package_name: "com.android.wm.shell.flicker.splitscreen",
+    instrumentation_target_package: "com.android.wm.shell.flicker.splitscreen",
+    srcs: [
+        ":WMShellFlickerTestsSplitScreenBase-src",
+        ":WMShellFlickerTestsSplitScreenGroup2-src",
+    ],
+    exclude_srcs: [
+        ":WMShellFlickerTestsSplitScreenGroup1-src",
+    ],
+    static_libs: ["WMShellFlickerTestsBase"],
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidManifest.xml
similarity index 75%
copy from libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml
copy to libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidManifest.xml
index ae130b8..9ff2161 100644
--- a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidManifest.xml
@@ -1,22 +1,22 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 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) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           xmlns:tools="http://schemas.android.com/tools"
-          package="com.android.wm.shell.flicker">
+          package="com.android.wm.shell.flicker.splitscreen">
 
     <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/>
     <!-- Read and write traces from external storage -->
@@ -69,4 +69,9 @@
             android:authorities="${applicationId}.androidx-startup"
             tools:node="remove" />
     </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.wm.shell.flicker.splitscreen"
+                     android:label="WindowManager Flicker Tests">
+    </instrumentation>
 </manifest>
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml
new file mode 100644
index 0000000..1df1136
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<configuration description="Runs WindowManager Shell Flicker Tests {MODULE}">
+    <option name="test-tag" value="FlickerTests"/>
+    <!-- Needed for storing the perfetto trace files in the sdcard/test_results-->
+    <option name="isolated-storage" value="false"/>
+
+    <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+        <!-- keeps the screen on during tests -->
+        <option name="screen-always-on" value="on"/>
+        <!-- prevents the phone from restarting -->
+        <option name="force-skip-system-props" value="true"/>
+        <!-- set WM tracing verbose level to all -->
+        <option name="run-command" value="cmd window tracing level all"/>
+        <!-- set WM tracing to frame (avoid incomplete states) -->
+        <option name="run-command" value="cmd window tracing frame"/>
+        <!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests -->
+        <option name="run-command" value="pm disable com.google.android.internal.betterbug"/>
+        <!-- ensure lock screen mode is swipe -->
+        <option name="run-command" value="locksettings set-disabled false"/>
+        <!-- restart launcher to activate TAPL -->
+        <option name="run-command"
+                value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher"/>
+        <!-- Increase trace size: 20mb for WM and 80mb for SF -->
+        <option name="run-command" value="cmd window tracing size 20480"/>
+        <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/>
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <option name="test-user-token" value="%TEST_USER%"/>
+        <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
+        <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
+        <option name="run-command" value="settings put system show_touches 1"/>
+        <option name="run-command" value="settings put system pointer_location 1"/>
+        <option name="teardown-command"
+                value="settings delete secure show_ime_with_hard_keyboard"/>
+        <option name="teardown-command" value="settings delete system show_touches"/>
+        <option name="teardown-command" value="settings delete system pointer_location"/>
+        <option name="teardown-command"
+                value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/>
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true"/>
+        <option name="test-file-name" value="{MODULE}.apk"/>
+        <option name="test-file-name" value="FlickerTestApp.apk"/>
+    </target_preparer>
+    <!-- Enable mocking GPS location by the test app -->
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <option name="run-command"
+                value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location allow"/>
+        <option name="teardown-command"
+                value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location deny"/>
+    </target_preparer>
+
+    <!-- Needed for pushing the trace config file -->
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="push-file"
+                key="trace_config.textproto"
+                value="/data/misc/perfetto-traces/trace_config.textproto"
+        />
+        <!--Install the content provider automatically when we push some file in sdcard folder.-->
+        <!--Needed to avoid the installation during the test suite.-->
+        <option name="push-file" key="trace_config.textproto" value="/sdcard/sample.textproto"/>
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="package" value="{PACKAGE}"/>
+        <option name="shell-timeout" value="6600s"/>
+        <option name="test-timeout" value="6000s"/>
+        <option name="hidden-api-checks" value="false"/>
+        <option name="device-listeners" value="android.device.collectors.PerfettoListener"/>
+        <!-- PerfettoListener related arguments -->
+        <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/>
+        <option name="instrumentation-arg"
+                key="perfetto_config_file"
+                value="trace_config.textproto"
+        />
+        <option name="instrumentation-arg" key="per_run" value="true"/>
+    </test>
+    <!-- Needed for pulling the collected trace config on to the host -->
+    <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+        <option name="pull-pattern-keys" value="perfetto_file_path"/>
+        <option name="directory-keys"
+                value="/data/user/0/com.android.wm.shell.flicker/files"/>
+        <option name="directory-keys"
+                value="/data/user/0/com.android.wm.shell.flicker.bubbles/files"/>
+        <option name="directory-keys"
+                value="/data/user/0/com.android.wm.shell.flicker.pip/files"/>
+        <option name="directory-keys"
+                value="/data/user/0/com.android.wm.shell.flicker.splitscreen/files"/>
+        <option name="directory-keys"
+                value="/data/user/0/com.android.wm.shell.flicker.service/files"/>
+        <option name="collect-on-run-ended-only" value="true"/>
+        <option name="clean-up" value="true"/>
+    </metrics_collector>
+</configuration>
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/OWNERS b/libs/WindowManager/Shell/tests/flicker/splitscreen/OWNERS
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/OWNERS
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/OWNERS
diff --git a/libs/WindowManager/Shell/tests/flicker/res/xml/network_security_config.xml b/libs/WindowManager/Shell/tests/flicker/splitscreen/res/xml/network_security_config.xml
similarity index 100%
copy from libs/WindowManager/Shell/tests/flicker/res/xml/network_security_config.xml
copy to libs/WindowManager/Shell/tests/flicker/splitscreen/res/xml/network_security_config.xml
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt
similarity index 90%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt
index 1387536..715a533 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt
@@ -62,10 +62,22 @@
         get() = {
             setup {
                 tapl.goHome()
-                SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
-                    secondaryApp, flicker.scenario.startRotation)
-                SplitScreenUtils.enterSplit(wmHelper, tapl, device, thirdApp, pipApp,
-                    flicker.scenario.startRotation)
+                SplitScreenUtils.enterSplit(
+                    wmHelper,
+                    tapl,
+                    device,
+                    primaryApp,
+                    secondaryApp,
+                    flicker.scenario.startRotation
+                )
+                SplitScreenUtils.enterSplit(
+                    wmHelper,
+                    tapl,
+                    device,
+                    thirdApp,
+                    pipApp,
+                    flicker.scenario.startRotation
+                )
                 pipApp.enableAutoEnterForPipActivity()
                 SplitScreenUtils.waitForSplitComplete(wmHelper, thirdApp, pipApp)
             }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
similarity index 88%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
index 3b9e53f..df1c9a2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
@@ -39,8 +39,16 @@
     protected val popupWindowLayer = ComponentNameMatcher("", "PopupWindow:")
     protected val thisTransition: FlickerBuilder.() -> Unit
         get() = {
-            setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
-                textEditApp, flicker.scenario.startRotation) }
+            setup {
+                SplitScreenUtils.enterSplit(
+                    wmHelper,
+                    tapl,
+                    device,
+                    primaryApp,
+                    textEditApp,
+                    flicker.scenario.startRotation
+                )
+            }
             transitions {
                 SplitScreenUtils.copyContentInSplit(
                     instrumentation,
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
similarity index 88%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
index 5fdde3a..d01eab0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
@@ -35,8 +35,16 @@
     SplitScreenBase(flicker) {
     protected val thisTransition: FlickerBuilder.() -> Unit
         get() = {
-            setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
-                secondaryApp, flicker.scenario.startRotation) }
+            setup {
+                SplitScreenUtils.enterSplit(
+                    wmHelper,
+                    tapl,
+                    device,
+                    primaryApp,
+                    secondaryApp,
+                    flicker.scenario.startRotation
+                )
+            }
             transitions {
                 if (tapl.isTablet) {
                     SplitScreenUtils.dragDividerToDismissSplit(
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
similarity index 87%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
index b7f6bfe..e36bd33 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
@@ -36,8 +36,14 @@
     protected val thisTransition: FlickerBuilder.() -> Unit
         get() = {
             setup {
-                SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp,
-                    flicker.scenario.startRotation)
+                SplitScreenUtils.enterSplit(
+                    wmHelper,
+                    tapl,
+                    device,
+                    primaryApp,
+                    secondaryApp,
+                    flicker.scenario.startRotation
+                )
             }
             transitions {
                 tapl.goHome()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
similarity index 86%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
index bb2a7aa..050d389 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
@@ -37,8 +37,16 @@
     SplitScreenBase(flicker) {
     protected val thisTransition: FlickerBuilder.() -> Unit
         get() = {
-            setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
-                secondaryApp, flicker.scenario.startRotation) }
+            setup {
+                SplitScreenUtils.enterSplit(
+                    wmHelper,
+                    tapl,
+                    device,
+                    primaryApp,
+                    secondaryApp,
+                    flicker.scenario.startRotation
+                )
+            }
             transitions { SplitScreenUtils.dragDividerToResizeAndWait(device, wmHelper) }
         }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SplitScreenBase.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SplitScreenBase.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SplitScreenBase.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SplitScreenBase.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
similarity index 94%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
index 46b0bd2..e39c3c9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
@@ -39,8 +39,16 @@
     SplitScreenBase(flicker) {
     protected val thisTransition: FlickerBuilder.() -> Unit
         get() = {
-            setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
-                secondaryApp, flicker.scenario.startRotation) }
+            setup {
+                SplitScreenUtils.enterSplit(
+                    wmHelper,
+                    tapl,
+                    device,
+                    primaryApp,
+                    secondaryApp,
+                    flicker.scenario.startRotation
+                )
+            }
             transitions {
                 SplitScreenUtils.doubleTapDividerToSwitch(device)
                 wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
similarity index 90%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
index baf7693..32284ba 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
@@ -39,8 +39,14 @@
     protected val thisTransition: FlickerBuilder.() -> Unit
         get() = {
             setup {
-                SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
-                    secondaryApp, flicker.scenario.startRotation)
+                SplitScreenUtils.enterSplit(
+                    wmHelper,
+                    tapl,
+                    device,
+                    primaryApp,
+                    secondaryApp,
+                    flicker.scenario.startRotation
+                )
 
                 thirdApp.launchViaIntent(wmHelper)
                 wmHelper.StateSyncBuilder().withWindowSurfaceAppeared(thirdApp).waitForAndVerify()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
similarity index 89%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
index 33b55f1..a926ec9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
@@ -37,8 +37,14 @@
     protected val thisTransition: FlickerBuilder.() -> Unit
         get() = {
             setup {
-                SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
-                    secondaryApp, flicker.scenario.startRotation)
+                SplitScreenUtils.enterSplit(
+                    wmHelper,
+                    tapl,
+                    device,
+                    primaryApp,
+                    secondaryApp,
+                    flicker.scenario.startRotation
+                )
 
                 tapl.goHome()
                 wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
similarity index 89%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
index b79dfb5..d2e1d52 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
@@ -37,8 +37,14 @@
     protected val thisTransition: FlickerBuilder.() -> Unit
         get() = {
             setup {
-                SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
-                    secondaryApp, flicker.scenario.startRotation)
+                SplitScreenUtils.enterSplit(
+                    wmHelper,
+                    tapl,
+                    device,
+                    primaryApp,
+                    secondaryApp,
+                    flicker.scenario.startRotation
+                )
 
                 tapl.goHome()
                 wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
similarity index 82%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
index 0204d75..9d6b251 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
@@ -39,10 +39,22 @@
     protected val thisTransition: FlickerBuilder.() -> Unit
         get() = {
             setup {
-                SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp,
-                    secondaryApp, flicker.scenario.startRotation)
-                SplitScreenUtils.enterSplit(wmHelper, tapl, device, thirdApp, fourthApp,
-                    flicker.scenario.startRotation)
+                SplitScreenUtils.enterSplit(
+                    wmHelper,
+                    tapl,
+                    device,
+                    primaryApp,
+                    secondaryApp,
+                    flicker.scenario.startRotation
+                )
+                SplitScreenUtils.enterSplit(
+                    wmHelper,
+                    tapl,
+                    device,
+                    thirdApp,
+                    fourthApp,
+                    flicker.scenario.startRotation
+                )
                 SplitScreenUtils.waitForSplitComplete(wmHelper, thirdApp, fourthApp)
             }
             transitions {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/trace_config/trace_config.textproto b/libs/WindowManager/Shell/tests/flicker/splitscreen/trace_config/trace_config.textproto
new file mode 100644
index 0000000..406ada9
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/trace_config/trace_config.textproto
@@ -0,0 +1,75 @@
+# 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.
+
+# proto-message: TraceConfig
+
+# Enable periodic flushing of the trace buffer into the output file.
+write_into_file: true
+
+# Writes the userspace buffer into the file every 1s.
+file_write_period_ms: 2500
+
+# See b/126487238 - we need to guarantee ordering of events.
+flush_period_ms: 30000
+
+# The trace buffers needs to be big enough to hold |file_write_period_ms| of
+# trace data. The trace buffer sizing depends on the number of trace categories
+# enabled and the device activity.
+
+# RSS events
+buffers: {
+  size_kb: 63488
+  fill_policy: RING_BUFFER
+}
+
+data_sources {
+  config {
+    name: "linux.process_stats"
+    target_buffer: 0
+    # polled per-process memory counters and process/thread names.
+    # If you don't want the polled counters, remove the "process_stats_config"
+    # section, but keep the data source itself as it still provides on-demand
+    # thread/process naming for ftrace data below.
+    process_stats_config {
+      scan_all_processes_on_start: true
+    }
+  }
+}
+
+data_sources: {
+  config {
+    name: "linux.ftrace"
+    ftrace_config {
+      ftrace_events: "ftrace/print"
+      ftrace_events: "task/task_newtask"
+      ftrace_events: "task/task_rename"
+      atrace_categories: "ss"
+      atrace_categories: "wm"
+      atrace_categories: "am"
+      atrace_categories: "aidl"
+      atrace_categories: "input"
+      atrace_categories: "binder_driver"
+      atrace_categories: "sched_process_exit"
+      atrace_apps: "com.android.server.wm.flicker.testapp"
+      atrace_apps: "com.android.systemui"
+      atrace_apps: "com.android.wm.shell.flicker"
+      atrace_apps: "com.android.wm.shell.flicker.other"
+      atrace_apps: "com.android.wm.shell.flicker.bubbles"
+      atrace_apps: "com.android.wm.shell.flicker.pip"
+      atrace_apps: "com.android.wm.shell.flicker.splitscreen"
+      atrace_apps: "com.google.android.apps.nexuslauncher"
+    }
+  }
+}
+
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt
index 735fbfb..568650d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * 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.
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt
index 6b3cfaf..c31b9e2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt
@@ -24,6 +24,7 @@
 import android.tools.common.traces.component.IComponentMatcher
 import android.tools.common.traces.component.IComponentNameMatcher
 import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.flicker.rules.ChangeDisplayOrientationRule
 import android.tools.device.traces.parsers.WindowManagerStateHelper
 import android.tools.device.traces.parsers.toFlickerComponent
 import android.view.InputDevice
@@ -42,7 +43,6 @@
 import com.android.server.wm.flicker.testapp.ActivityOptions
 import com.android.server.wm.flicker.testapp.ActivityOptions.SplitScreen.Primary
 import org.junit.Assert.assertNotNull
-import android.tools.device.flicker.rules.ChangeDisplayOrientationRule
 
 object SplitScreenUtils {
     private const val TIMEOUT_MS = 3_000L
@@ -153,15 +153,10 @@
         } else {
             val rotationCheckEnabled = tapl.getExpectedRotationCheckEnabled()
             tapl.setExpectedRotationCheckEnabled(false) // disable rotation check to enter overview
-            val home = tapl.workspace
-                .switchToOverview()
+            val home = tapl.workspace.switchToOverview()
             tapl.setExpectedRotationCheckEnabled(rotationCheckEnabled) // restore rotation checks
             ChangeDisplayOrientationRule.setRotation(rotation)
-            home.currentTask
-                .tapMenu()
-                .tapSplitMenuItem()
-                .currentTask
-                .open()
+            home.currentTask.tapMenu().tapSplitMenuItem().currentTask.open()
         }
         SystemClock.sleep(TIMEOUT_MS)
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
new file mode 100644
index 0000000..7a8a2a9
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
@@ -0,0 +1,174 @@
+/*
+ * 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.wm.shell.transition;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.view.WindowManager.TRANSIT_TO_BACK;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.app.WindowConfiguration.ActivityType;
+import android.content.Context;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.view.SurfaceControl;
+import android.window.TransitionInfo;
+import android.window.TransitionInfo.TransitionMode;
+import android.window.WindowOrganizer;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.TestShellExecutor;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tests for the home transition observer.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class HomeTransitionObserverTest extends ShellTestCase {
+
+    private final WindowOrganizer mOrganizer = mock(WindowOrganizer.class);
+    private final TransactionPool mTransactionPool = mock(TransactionPool.class);
+    private final Context mContext =
+            InstrumentationRegistry.getInstrumentation().getTargetContext();
+    private final ShellExecutor mAnimExecutor = new TestShellExecutor();
+    private final TestShellExecutor mMainExecutor = new TestShellExecutor();
+    private final Handler mMainHandler = new Handler(Looper.getMainLooper());
+    private final DisplayController mDisplayController = mock(DisplayController.class);
+
+    private Transitions mTransition;
+
+    @Before
+    public void setUp() {
+        mTransition = new Transitions(mContext, mock(ShellInit.class), mock(ShellController.class),
+                mOrganizer, mTransactionPool, mDisplayController, mMainExecutor,
+                mMainHandler, mAnimExecutor);
+    }
+
+    @Test
+    public void testHomeActivityWithOpenModeNotifiesHomeIsVisible() throws RemoteException {
+        IHomeTransitionListener listener = mock(IHomeTransitionListener.class);
+        when(listener.asBinder()).thenReturn(mock(IBinder.class));
+
+        HomeTransitionObserver observer = new HomeTransitionObserver(mContext, mMainExecutor,
+                mTransition);
+        observer.setHomeTransitionListener(listener);
+
+        TransitionInfo info = mock(TransitionInfo.class);
+        TransitionInfo.Change change = mock(TransitionInfo.Change.class);
+        ActivityManager.RunningTaskInfo taskInfo = mock(ActivityManager.RunningTaskInfo.class);
+        when(change.getTaskInfo()).thenReturn(taskInfo);
+        when(info.getChanges()).thenReturn(new ArrayList<>(List.of(change)));
+
+        setupTransitionInfo(taskInfo, change, ACTIVITY_TYPE_HOME, TRANSIT_OPEN);
+
+        observer.onTransitionReady(mock(IBinder.class),
+                info,
+                mock(SurfaceControl.Transaction.class),
+                mock(SurfaceControl.Transaction.class));
+
+        verify(listener, times(1)).onHomeVisibilityChanged(true);
+    }
+
+    @Test
+    public void testHomeActivityWithCloseModeNotifiesHomeIsNotVisible() throws RemoteException {
+        IHomeTransitionListener listener = mock(IHomeTransitionListener.class);
+        when(listener.asBinder()).thenReturn(mock(IBinder.class));
+
+        HomeTransitionObserver observer = new HomeTransitionObserver(mContext, mMainExecutor,
+                mTransition);
+        observer.setHomeTransitionListener(listener);
+
+        TransitionInfo info = mock(TransitionInfo.class);
+        TransitionInfo.Change change = mock(TransitionInfo.Change.class);
+        ActivityManager.RunningTaskInfo taskInfo = mock(ActivityManager.RunningTaskInfo.class);
+        when(change.getTaskInfo()).thenReturn(taskInfo);
+        when(info.getChanges()).thenReturn(new ArrayList<>(List.of(change)));
+
+        setupTransitionInfo(taskInfo, change, ACTIVITY_TYPE_HOME, TRANSIT_TO_BACK);
+
+        observer.onTransitionReady(mock(IBinder.class),
+                info,
+                mock(SurfaceControl.Transaction.class),
+                mock(SurfaceControl.Transaction.class));
+
+        verify(listener, times(1)).onHomeVisibilityChanged(false);
+    }
+
+    @Test
+    public void testNonHomeActivityDoesNotTriggerCallback() throws RemoteException {
+        IHomeTransitionListener listener = mock(IHomeTransitionListener.class);
+        when(listener.asBinder()).thenReturn(mock(IBinder.class));
+
+        HomeTransitionObserver observer = new HomeTransitionObserver(mContext, mMainExecutor,
+                mTransition);
+        observer.setHomeTransitionListener(listener);
+
+        TransitionInfo info = mock(TransitionInfo.class);
+        TransitionInfo.Change change = mock(TransitionInfo.Change.class);
+        ActivityManager.RunningTaskInfo taskInfo = mock(ActivityManager.RunningTaskInfo.class);
+        when(change.getTaskInfo()).thenReturn(taskInfo);
+        when(info.getChanges()).thenReturn(new ArrayList<>(List.of(change)));
+
+
+        setupTransitionInfo(taskInfo, change, ACTIVITY_TYPE_UNDEFINED, TRANSIT_TO_BACK);
+
+        observer.onTransitionReady(mock(IBinder.class),
+                info,
+                mock(SurfaceControl.Transaction.class),
+                mock(SurfaceControl.Transaction.class));
+
+        verify(listener, times(0)).onHomeVisibilityChanged(anyBoolean());
+    }
+
+    /**
+     * Helper class to initialize variables for the rest.
+     */
+    private void setupTransitionInfo(ActivityManager.RunningTaskInfo taskInfo,
+            TransitionInfo.Change change,
+            @ActivityType int activityType,
+            @TransitionMode int mode) {
+        when(taskInfo.getActivityType()).thenReturn(activityType);
+        when(change.getMode()).thenReturn(mode);
+    }
+
+}
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index a4890ed..ad963dd 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -19,6 +19,8 @@
 #include "DeferredLayerUpdater.h"
 #include "hwui/Paint.h"
 
+#include <hwui/MinikinSkia.h>
+#include <hwui/Typeface.h>
 #include <minikin/Layout.h>
 #include <pipeline/skia/SkiaOpenGLPipeline.h>
 #include <pipeline/skia/SkiaVulkanPipeline.h>
@@ -179,5 +181,13 @@
     return outlineInLocalCoord;
 }
 
+SkFont TestUtils::defaultFont() {
+    const std::shared_ptr<minikin::MinikinFont>& minikinFont =
+      Typeface::resolveDefault(nullptr)->fFontCollection->getFamilyAt(0)->getFont(0)->baseTypeface();
+    SkTypeface* skTypeface = reinterpret_cast<const MinikinFontSkia*>(minikinFont.get())->GetSkTypeface();
+    LOG_ALWAYS_FATAL_IF(skTypeface == nullptr);
+    return SkFont(sk_ref_sp(skTypeface));
+}
+
 } /* namespace uirenderer */
 } /* namespace android */
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index ffc664c..0ede902 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -30,6 +30,7 @@
 
 #include <SkBitmap.h>
 #include <SkColor.h>
+#include <SkFont.h>
 #include <SkImageInfo.h>
 #include <SkRefCnt.h>
 
@@ -353,6 +354,8 @@
 
     static CallCounts& countsForFunctor(int functor) { return sMockFunctorCounts[functor]; }
 
+    static SkFont defaultFont();
+
 private:
     static std::unordered_map<int, CallCounts> sMockFunctorCounts;
 
diff --git a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp
index 4a5d946..97d4c82 100644
--- a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp
@@ -57,7 +57,7 @@
                 128 * 3;
         paint.setColor(bgDark ? Color::White : Color::Grey_700);
 
-        SkFont font;
+        SkFont font = TestUtils::defaultFont();
         font.setSize(size / 2);
         char charToShow = 'A' + (rand() % 26);
         const SkPoint pos = {SkIntToScalar(size / 2),
diff --git a/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp b/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp
index bb95490..159541c 100644
--- a/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp
@@ -102,7 +102,7 @@
                 128 * 3;
         paint.setColor(bgDark ? Color::White : Color::Grey_700);
 
-        SkFont font;
+        SkFont font = TestUtils::defaultFont();
         font.setSize(size / 2);
         char charToShow = 'A' + (rand() % 26);
         const SkPoint pos = {SkIntToScalar(size / 2),
diff --git a/location/api/system-lint-baseline.txt b/location/api/system-lint-baseline.txt
index a5e5752..043a082 100644
--- a/location/api/system-lint-baseline.txt
+++ b/location/api/system-lint-baseline.txt
@@ -9,3 +9,9 @@
     SAM-compatible parameters (such as parameter 2, "listener", in android.location.LocationManager.requestSingleUpdate) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
 SamShouldBeLast: android.location.LocationManager#requestSingleUpdate(android.location.Criteria, android.location.LocationListener, android.os.Looper):
     SAM-compatible parameters (such as parameter 2, "listener", in android.location.LocationManager.requestSingleUpdate) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+
+
+UnflaggedApi: android.location.GnssMeasurementRequest#getWorkSource():
+    New API must be flagged with @FlaggedApi: method android.location.GnssMeasurementRequest.getWorkSource()
+UnflaggedApi: android.location.GnssMeasurementRequest.Builder#setWorkSource(android.os.WorkSource):
+    New API must be flagged with @FlaggedApi: method android.location.GnssMeasurementRequest.Builder.setWorkSource(android.os.WorkSource)
diff --git a/media/java/android/media/projection/IMediaProjectionManager.aidl b/media/java/android/media/projection/IMediaProjectionManager.aidl
index 24efbd1..a7ec6c6 100644
--- a/media/java/android/media/projection/IMediaProjectionManager.aidl
+++ b/media/java/android/media/projection/IMediaProjectionManager.aidl
@@ -212,4 +212,9 @@
     @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
             + ".permission.MANAGE_MEDIA_PROJECTION)")
     oneway void notifyAppSelectorDisplayed(int hostUid);
+
+    @EnforcePermission("MANAGE_MEDIA_PROJECTION")
+    @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
+            + ".permission.MANAGE_MEDIA_PROJECTION)")
+    void notifyWindowingModeChanged(int contentToRecord, int targetUid, int windowingMode);
 }
diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index 9147c12..db01950 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -2534,6 +2534,67 @@
          */
         public static final String COLUMN_BROADCAST_GENRE = Programs.COLUMN_BROADCAST_GENRE;
 
+        /**
+         * The broadcast visibility type of this TV channel.
+         *
+         * <p>This is used to indicate the broadcast visibility type defined in the underlying
+         * broadcast standard or country/operator profile, if applicable. For example,
+         * {@code visible_service_flag} and {@code numeric_selection_flag} of
+         * {@code service_attribute_descriptor} in D-Book, {@code visible_service_flag} and
+         * {@code selectable_service_flag} of {@code ciplus_service_descriptor} in CI Plus 1.3
+         * specification.
+         *
+         * <p>The value should match one of the following:
+         * {@link #BROADCAST_VISIBILITY_TYPE_VISIBLE},
+         * {@link #BROADCAST_VISIBILITY_TYPE_NUMERIC_SELECTABLE_ONLY}, and
+         * {@link #BROADCAST_VISIBILITY_TYPE_INVISIBLE}.
+         *
+         * <p>If not specified, this value is set to {@link #BROADCAST_VISIBILITY_TYPE_VISIBLE}
+         * by default.
+         *
+         * <p>Type: INTEGER
+         * @hide
+         */
+        public static final String COLUMN_BROADCAST_VISIBILITY_TYPE = "broadcast_visibility_type";
+
+        /** @hide */
+        @IntDef(prefix = { "BROADCAST_VISIBILITY_TYPE_" }, value = {
+                BROADCAST_VISIBILITY_TYPE_VISIBLE,
+                BROADCAST_VISIBILITY_TYPE_NUMERIC_SELECTABLE_ONLY,
+                BROADCAST_VISIBILITY_TYPE_INVISIBLE,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface BroadcastVisibilityType {}
+
+        /**
+         * The broadcast visibility type for visible services. Use this type when the service is
+         * visible from users and selectable by users via normal service navigation mechanisms.
+         *
+         * @see #COLUMN_BROADCAST_VISIBILITY_TYPE
+         * @hide
+         */
+        public static final int BROADCAST_VISIBILITY_TYPE_VISIBLE = 0;
+
+        /**
+         * The broadcast visibility type for numeric selectable only services. Use this type when
+         * the service is invisible from users but selectable by users only via direct entry of
+         * the logical channel number.
+         *
+         * @see #COLUMN_BROADCAST_VISIBILITY_TYPE
+         * @hide
+         */
+        public static final int BROADCAST_VISIBILITY_TYPE_NUMERIC_SELECTABLE_ONLY = 1;
+
+        /**
+         * The broadcast visibility type for invisible services. Use this type when the service
+         * is invisible from users and unselectable by users via any of normal service navigation
+         * mechanisms.
+         *
+         * @see #COLUMN_BROADCAST_VISIBILITY_TYPE
+         * @hide
+         */
+        public static final int BROADCAST_VISIBILITY_TYPE_INVISIBLE = 2;
+
         private Channels() {}
 
         /**
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 8964ada..b9dc618 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -14,6 +14,7 @@
         "androidx.localbroadcastmanager_localbroadcastmanager",
         "androidx.room_room-runtime",
         "zxing-core",
+        "guava",
 
         "WifiTrackerLibRes",
         "iconloader",
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 6eaabbb..96029c8 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -202,10 +202,18 @@
     <string name="bluetooth_active_battery_level_untethered">Active, L: <xliff:g id="battery_level_as_percentage" example="25%">%1$s</xliff:g> battery, R: <xliff:g id="battery_level_as_percentage" example="25%">%2$s</xliff:g> battery</string>
     <!-- Connected devices settings. Message when Bluetooth is connected but not in use, showing remote device battery level. [CHAR LIMIT=NONE] -->
     <string name="bluetooth_battery_level"><xliff:g id="battery_level_as_percentage">%1$s</xliff:g> battery</string>
+    <!-- Connected devices settings. Message on TV when Bluetooth is connected but not in use, showing remote device battery level. [CHAR LIMIT=NONE] -->
+    <string name="tv_bluetooth_battery_level">Battery <xliff:g id="battery_level_as_percentage">%1$s</xliff:g></string>
     <!-- Connected devices settings. Message when Bluetooth is connected but not in use, showing remote device battery level for untethered headset. [CHAR LIMIT=NONE] -->
     <string name="bluetooth_battery_level_untethered">L: <xliff:g id="battery_level_as_percentage" example="25%">%1$s</xliff:g> battery, R: <xliff:g id="battery_level_as_percentage" example="25%">%2$s</xliff:g> battery</string>
+    <!-- Connected devices settings. Message when Bluetooth is connected but not in use, showing remote device battery level for the left part of the untethered headset. [CHAR LIMIT=NONE] -->
+    <string name="bluetooth_battery_level_untethered_left">Left <xliff:g id="battery_level_as_percentage" example="25%">%1$s</xliff:g></string>
+    <!-- Connected devices settings. Message when Bluetooth is connected but not in use, showing remote device battery level for the right part of the untethered headset. [CHAR LIMIT=NONE] -->
+    <string name="bluetooth_battery_level_untethered_right">Right <xliff:g id="battery_level_as_percentage" example="25%">%1$s</xliff:g></string>
     <!-- Connected devices settings. Message when Bluetooth is connected and active but no battery information, showing remote device status. [CHAR LIMIT=NONE] -->
     <string name="bluetooth_active_no_battery_level">Active</string>
+    <!-- Connected devices settings. Message shown when bluetooth device is disconnected but is a known, previously connected device [CHAR LIMIT=NONE] -->
+    <string name="bluetooth_saved_device">Saved</string>
 
     <!-- Connected device settings. Message when the left-side hearing aid device is active. [CHAR LIMIT=NONE] -->
     <string name="bluetooth_hearing_aid_left_active">Active, left only</string>
@@ -1019,6 +1027,13 @@
     <!-- Settings item title to select whether to disable cache for transcoding. [CHAR LIMIT=85] -->
     <string name="transcode_disable_cache">Disable transcoding cache</string>
 
+    <!-- Developer settings title: widevine settings screen. [CHAR LIMIT=50] -->
+    <string name="widevine_settings_title">Widevine settings</string>
+     <!-- Developer settings title: select whether to enable Force L3 fallback. [CHAR LIMIT=50] -->
+    <string name="force_l3_fallback_title">Force L3 fallback</string>
+     <!-- Developer settings summary: select whether to enable Force L3 fallback.[CHAR LIMIT=NONE] -->
+    <string name="force_l3_fallback_summary">Select to force L3 fallback</string>
+
     <!-- Services settings screen, setting option name for the user to go to the screen to view running services -->
     <string name="runningservices_settings_title">Running services</string>
     <!-- Services settings screen, setting option summary for the user to go to the screen to view running services  -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java b/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
index cac3103..07de7fd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
@@ -281,7 +281,7 @@
 
         for (int i = 0; i < Math.min(appEntries.size(), number); i++) {
             final ApplicationsState.AppEntry entry = appEntries.get(i);
-            ThreadUtils.postOnBackgroundThread(() -> {
+            var unused = ThreadUtils.getBackgroundExecutor().submit(() -> {
                 getIcon(context, entry);
             });
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index 96bb4b5..079cde0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -1679,7 +1679,7 @@
             ensureLabel(context);
             // Speed up the cache of the label description if they haven't been created.
             if (this.labelDescription == null) {
-                ThreadUtils.postOnBackgroundThread(
+                var unused = ThreadUtils.getBackgroundExecutor().submit(
                         () -> this.ensureLabelDescriptionLocked(context));
             }
             UserManager um = UserManager.get(context);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index b0832e3..24083b6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -35,7 +35,9 @@
 import android.os.Message;
 import android.os.ParcelUuid;
 import android.os.SystemClock;
+import android.text.SpannableStringBuilder;
 import android.text.TextUtils;
+import android.text.style.ForegroundColorSpan;
 import android.util.Log;
 import android.util.LruCache;
 import android.util.Pair;
@@ -46,9 +48,14 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.settingslib.R;
 import com.android.settingslib.Utils;
+import com.android.settingslib.media.flags.Flags;
 import com.android.settingslib.utils.ThreadUtils;
 import com.android.settingslib.widget.AdaptiveOutlineDrawable;
 
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+
 import java.sql.Timestamp;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -79,6 +86,12 @@
     private static final long MAX_LEAUDIO_DELAY_FOR_AUTO_CONNECT = 30000;
     private static final long MAX_MEDIA_PROFILE_CONNECT_DELAY = 60000;
 
+    private static final int DEFAULT_LOW_BATTERY_THRESHOLD = 20;
+
+    // To be used instead of a resource id to indicate that low battery states should not be
+    // changed to a different color.
+    private static final int SUMMARY_NO_COLOR_FOR_LOW_BATTERY = 0;
+
     private final Context mContext;
     private final BluetoothAdapter mLocalAdapter;
     private final LocalBluetoothProfileManager mProfileManager;
@@ -708,7 +721,7 @@
 
 
     void refresh() {
-        ThreadUtils.postOnBackgroundThread(() -> {
+        ListenableFuture<Void> future = ThreadUtils.getBackgroundExecutor().submit(() -> {
             if (BluetoothUtils.isAdvancedDetailsHeader(mDevice)) {
                 Uri uri = BluetoothUtils.getUriMetaData(getDevice(),
                         BluetoothDevice.METADATA_MAIN_ICON);
@@ -718,11 +731,17 @@
                                     mContext, this).first);
                 }
             }
-
-            ThreadUtils.postOnMainThread(() -> {
-                dispatchAttributesChanged();
-            });
+            return null;
         });
+        Futures.addCallback(future, new FutureCallback<>() {
+            @Override
+            public void onSuccess(Void result) {
+                dispatchAttributesChanged();
+            }
+
+            @Override
+            public void onFailure(Throwable t) {}
+        }, mContext.getMainExecutor());
     }
 
     public void setJustDiscovered(boolean justDiscovered) {
@@ -1179,6 +1198,46 @@
      * @param shortSummary {@code true} if need to return short version summary
      */
     public String getConnectionSummary(boolean shortSummary) {
+        CharSequence summary = getConnectionSummary(shortSummary, false /* isTvSummary */,
+                SUMMARY_NO_COLOR_FOR_LOW_BATTERY);
+        if (summary != null) {
+            return summary.toString();
+        }
+        return null;
+    }
+
+    /**
+     * Returns android tv string that describes the connection state of this device.
+     */
+    public CharSequence getTvConnectionSummary() {
+        return getTvConnectionSummary(SUMMARY_NO_COLOR_FOR_LOW_BATTERY);
+    }
+
+    /**
+     * Returns android tv string that describes the connection state of this device, with low
+     * battery states highlighted in color.
+     *
+     * @param lowBatteryColorRes - resource id for the color that should be used for the part of the
+     *                           CharSequence that contains low battery information.
+     */
+    public CharSequence getTvConnectionSummary(int lowBatteryColorRes) {
+        return getConnectionSummary(false /* shortSummary */, true /* isTvSummary */,
+                lowBatteryColorRes);
+    }
+
+    /**
+     * Return summary that describes connection state of this device. Summary depends on:
+     * 1. Whether device has battery info
+     * 2. Whether device is in active usage(or in phone call)
+     *
+     * @param shortSummary       {@code true} if need to return short version summary
+     * @param isTvSummary        {@code true} if the summary should be TV specific
+     * @param lowBatteryColorRes Resource id of the color to be used for low battery strings. Use
+     *                           {@link SUMMARY_NO_COLOR_FOR_LOW_BATTERY} if no separate color
+     *                           should be used.
+     */
+    private CharSequence getConnectionSummary(boolean shortSummary, boolean isTvSummary,
+            int lowBatteryColorRes) {
         boolean profileConnected = false;    // Updated as long as BluetoothProfile is connected
         boolean a2dpConnected = true;        // A2DP is connected
         boolean hfpConnected = true;         // HFP is connected
@@ -1305,17 +1364,82 @@
             }
         }
 
-        if (stringRes != R.string.bluetooth_pairing
-                || getBondState() == BluetoothDevice.BOND_BONDING) {
-            if (isTwsBatteryAvailable(leftBattery, rightBattery)) {
-                return mContext.getString(stringRes, Utils.formatPercentage(leftBattery),
-                        Utils.formatPercentage(rightBattery));
-            } else {
-                return mContext.getString(stringRes, batteryLevelPercentageString);
-            }
-        } else {
+        if (stringRes == R.string.bluetooth_pairing
+                && getBondState() != BluetoothDevice.BOND_BONDING) {
             return null;
         }
+
+        boolean summaryIncludesBatteryLevel = stringRes == R.string.bluetooth_battery_level
+                || stringRes == R.string.bluetooth_active_battery_level
+                || stringRes == R.string.bluetooth_active_battery_level_untethered
+                || stringRes == R.string.bluetooth_battery_level_untethered;
+        if (isTvSummary && summaryIncludesBatteryLevel && Flags.enableTvMediaOutputDialog()) {
+            return getTvBatterySummary(batteryLevel, leftBattery, rightBattery, lowBatteryColorRes);
+        }
+
+        if (isTwsBatteryAvailable(leftBattery, rightBattery)) {
+            return mContext.getString(stringRes, Utils.formatPercentage(leftBattery),
+                    Utils.formatPercentage(rightBattery));
+        } else {
+            return mContext.getString(stringRes, batteryLevelPercentageString);
+        }
+    }
+
+    private CharSequence getTvBatterySummary(int mainBattery, int leftBattery, int rightBattery,
+            int lowBatteryColorRes) {
+        // Since there doesn't seem to be a way to use format strings to add the
+        // percentages and also mark which part of the string is left and right to color
+        // them, we are using one string resource per battery.
+        Resources res = mContext.getResources();
+        SpannableStringBuilder spannableBuilder = new SpannableStringBuilder();
+        if (leftBattery >= 0 || rightBattery >= 0) {
+            // Not switching the left and right for RTL to keep the left earbud always on
+            // the left.
+            if (leftBattery >= 0) {
+                String left = res.getString(
+                        R.string.bluetooth_battery_level_untethered_left,
+                        Utils.formatPercentage(leftBattery));
+                addBatterySpan(spannableBuilder, left, isBatteryLow(leftBattery,
+                                BluetoothDevice.METADATA_UNTETHERED_LEFT_LOW_BATTERY_THRESHOLD),
+                        lowBatteryColorRes);
+            }
+            if (rightBattery >= 0) {
+                if (!spannableBuilder.isEmpty()) {
+                    spannableBuilder.append(" ");
+                }
+                String right = res.getString(
+                        R.string.bluetooth_battery_level_untethered_right,
+                        Utils.formatPercentage(rightBattery));
+                addBatterySpan(spannableBuilder, right, isBatteryLow(rightBattery,
+                                BluetoothDevice.METADATA_UNTETHERED_RIGHT_LOW_BATTERY_THRESHOLD),
+                        lowBatteryColorRes);
+            }
+        } else {
+            addBatterySpan(spannableBuilder, res.getString(R.string.tv_bluetooth_battery_level,
+                            Utils.formatPercentage(mainBattery)),
+                    isBatteryLow(mainBattery, BluetoothDevice.METADATA_MAIN_LOW_BATTERY_THRESHOLD),
+                    lowBatteryColorRes);
+        }
+        return spannableBuilder;
+    }
+
+    private void addBatterySpan(SpannableStringBuilder builder,
+            String batteryString, boolean lowBattery, int lowBatteryColorRes) {
+        if (lowBattery && lowBatteryColorRes != SUMMARY_NO_COLOR_FOR_LOW_BATTERY) {
+            builder.append(batteryString,
+                    new ForegroundColorSpan(mContext.getResources().getColor(lowBatteryColorRes)),
+                    0 /* flags */);
+        } else {
+            builder.append(batteryString);
+        }
+    }
+
+    private boolean isBatteryLow(int batteryLevel, int metadataKey) {
+        int lowBatteryThreshold = BluetoothUtils.getIntMetaData(mDevice, metadataKey);
+        if (lowBatteryThreshold <= 0) {
+            lowBatteryThreshold = DEFAULT_LOW_BATTERY_THRESHOLD;
+        }
+        return batteryLevel <= lowBatteryThreshold;
     }
 
     private boolean isTwsBatteryAvailable(int leftBattery, int rightBattery) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractBluetoothAddressPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractBluetoothAddressPreferenceController.java
index 4fcdc8b..0b2b354 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractBluetoothAddressPreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractBluetoothAddressPreferenceController.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.text.TextUtils;
 
+import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 import androidx.preference.Preference;
 import androidx.preference.PreferenceScreen;
@@ -29,6 +30,10 @@
 import com.android.settingslib.core.lifecycle.Lifecycle;
 import com.android.settingslib.utils.ThreadUtils;
 
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+
 /**
  * Preference controller for bluetooth address
  */
@@ -75,9 +80,11 @@
     protected void updateConnectivity() {
         BluetoothAdapter bluetooth = BluetoothAdapter.getDefaultAdapter();
         if (bluetooth != null && mBtAddress != null) {
-            ThreadUtils.postOnBackgroundThread(() -> {
-                String address = bluetooth.isEnabled() ? bluetooth.getAddress() : null;
-                ThreadUtils.postOnMainThread(() -> {
+            ListenableFuture<String> future = ThreadUtils.getBackgroundExecutor()
+                    .submit(() -> bluetooth.isEnabled() ? bluetooth.getAddress() : null);
+            Futures.addCallback(future, new FutureCallback<>() {
+                @Override
+                public void onSuccess(@Nullable String address) {
                     if (!TextUtils.isEmpty(address)) {
                         // Convert the address to lowercase for consistency with the wifi MAC
                         // address.
@@ -85,8 +92,11 @@
                     } else {
                         mBtAddress.setSummary(R.string.status_unavailable);
                     }
-                });
-            });
+                }
+
+                @Override
+                public void onFailure(Throwable t) {}
+            }, mContext.getMainExecutor());
         }
     }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
index ed518f7..9560b8d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
@@ -72,6 +72,13 @@
     }
 
     @Override
+    public CharSequence getSummaryForTv(int lowBatteryColorRes) {
+        return isConnected() || mCachedDevice.isBusy()
+                ? mCachedDevice.getTvConnectionSummary(lowBatteryColorRes)
+                : mContext.getString(R.string.bluetooth_saved_device);
+    }
+
+    @Override
     public int getSelectionBehavior() {
         // We don't allow apps to override the selection behavior of system routes.
         return SELECTION_BEHAVIOR_TRANSFER;
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index 8085c99..c8e4c0c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -208,6 +208,17 @@
     public abstract String getSummary();
 
     /**
+     * Get summary from MediaDevice for TV with low batter states in a different color if
+     * applicable.
+     *
+     * @param lowBatteryColorRes Color resource for the part of the CharSequence that describes a
+     *                           low battery state.
+     */
+    public CharSequence getSummaryForTv(int lowBatteryColorRes) {
+        return getSummary();
+    }
+
+    /**
      * Get icon of MediaDevice.
      *
      * @return drawable of icon.
diff --git a/packages/SettingsLib/src/com/android/settingslib/qrcode/QrCamera.java b/packages/SettingsLib/src/com/android/settingslib/qrcode/QrCamera.java
index e651090..ae17acb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/qrcode/QrCamera.java
+++ b/packages/SettingsLib/src/com/android/settingslib/qrcode/QrCamera.java
@@ -116,8 +116,13 @@
             mDecodeTask = null;
         }
         if (mCamera != null) {
-            mCamera.stopPreview();
-            releaseCamera();
+            try {
+                mCamera.stopPreview();
+                releaseCamera();
+            } catch (RuntimeException e) {
+                Log.e(TAG, "Stop previewing camera failed:" + e);
+                mCamera = null;
+            }
         }
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/AvatarPhotoController.java b/packages/SettingsLib/src/com/android/settingslib/users/AvatarPhotoController.java
index 4ce88ee..f165c9f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/AvatarPhotoController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/AvatarPhotoController.java
@@ -36,20 +36,23 @@
 import android.util.EventLog;
 import android.util.Log;
 
+import androidx.annotation.Nullable;
 import androidx.core.content.FileProvider;
 
 import com.android.settingslib.utils.ThreadUtils;
 
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+
 import libcore.io.Streams;
 
 import java.io.File;
-import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.List;
-import java.util.concurrent.ExecutionException;
 
 class AvatarPhotoController {
 
@@ -71,6 +74,8 @@
         Uri createTempImageUri(File parentDir, String fileName, boolean purge);
 
         ContentResolver getContentResolver();
+
+        Context getContext();
     }
 
     private static final String TAG = "AvatarPhotoController";
@@ -163,14 +168,21 @@
     }
 
     private void copyAndCropPhoto(final Uri pictureUri, boolean delayBeforeCrop) {
-        try {
-            ThreadUtils.postOnBackgroundThread(() -> {
-                final ContentResolver cr = mContextInjector.getContentResolver();
-                try (InputStream in = cr.openInputStream(pictureUri);
-                        OutputStream out = cr.openOutputStream(mPreCropPictureUri)) {
-                    Streams.copy(in, out);
-                } catch (IOException e) {
-                    Log.w(TAG, "Failed to copy photo", e);
+        ListenableFuture<Uri> future = ThreadUtils.getBackgroundExecutor().submit(() -> {
+            final ContentResolver cr = mContextInjector.getContentResolver();
+            try (InputStream in = cr.openInputStream(pictureUri);
+                    OutputStream out = cr.openOutputStream(mPreCropPictureUri)) {
+                Streams.copy(in, out);
+                return mPreCropPictureUri;
+            } catch (IOException e) {
+                Log.w(TAG, "Failed to copy photo", e);
+                return null;
+            }
+        });
+        Futures.addCallback(future, new FutureCallback<>() {
+            @Override
+            public void onSuccess(@Nullable Uri result) {
+                if (result == null) {
                     return;
                 }
                 Runnable cropRunnable = () -> {
@@ -179,15 +191,18 @@
                     }
                 };
                 if (delayBeforeCrop) {
-                    ThreadUtils.postOnMainThreadDelayed(cropRunnable, DELAY_BEFORE_CROP_MILLIS);
+                    mContextInjector.getContext().getMainThreadHandler()
+                            .postDelayed(cropRunnable, DELAY_BEFORE_CROP_MILLIS);
                 } else {
-                    ThreadUtils.postOnMainThread(cropRunnable);
+                    cropRunnable.run();
                 }
+            }
 
-            }).get();
-        } catch (InterruptedException | ExecutionException e) {
-            Log.e(TAG, "Error performing copy-and-crop", e);
-        }
+            @Override
+            public void onFailure(Throwable t) {
+                Log.e(TAG, "Error performing copy-and-crop", t);
+            }
+        }, mContextInjector.getContext().getMainExecutor());
     }
 
     private void cropPhoto(final Uri pictureUri) {
@@ -225,44 +240,49 @@
     }
 
     private void onPhotoNotCropped(final Uri data) {
-        try {
-            ThreadUtils.postOnBackgroundThread(() -> {
-                // Scale and crop to a square aspect ratio
-                Bitmap croppedImage = Bitmap.createBitmap(mPhotoSize, mPhotoSize,
-                        Bitmap.Config.ARGB_8888);
-                Canvas canvas = new Canvas(croppedImage);
-                Bitmap fullImage;
-                try {
-                    InputStream imageStream = mContextInjector.getContentResolver()
-                            .openInputStream(data);
-                    fullImage = BitmapFactory.decodeStream(imageStream);
-                } catch (FileNotFoundException fe) {
-                    return;
-                }
-                if (fullImage != null) {
-                    int rotation = getRotation(data);
-                    final int squareSize = Math.min(fullImage.getWidth(),
-                            fullImage.getHeight());
-                    final int left = (fullImage.getWidth() - squareSize) / 2;
-                    final int top = (fullImage.getHeight() - squareSize) / 2;
+        ListenableFuture<Bitmap> future = ThreadUtils.getBackgroundExecutor().submit(() -> {
+            // Scale and crop to a square aspect ratio
+            Bitmap croppedImage = Bitmap.createBitmap(mPhotoSize, mPhotoSize,
+                    Bitmap.Config.ARGB_8888);
+            Canvas canvas = new Canvas(croppedImage);
+            Bitmap fullImage;
+            try (InputStream imageStream = mContextInjector.getContentResolver()
+                    .openInputStream(data)) {
+                fullImage = BitmapFactory.decodeStream(imageStream);
+            }
+            if (fullImage == null) {
+                Log.e(TAG, "Image data could not be decoded");
+                return null;
+            }
+            int rotation = getRotation(data);
+            final int squareSize = Math.min(fullImage.getWidth(),
+                    fullImage.getHeight());
+            final int left = (fullImage.getWidth() - squareSize) / 2;
+            final int top = (fullImage.getHeight() - squareSize) / 2;
 
-                    Matrix matrix = new Matrix();
-                    RectF rectSource = new RectF(left, top,
-                            left + squareSize, top + squareSize);
-                    RectF rectDest = new RectF(0, 0, mPhotoSize, mPhotoSize);
-                    matrix.setRectToRect(rectSource, rectDest, Matrix.ScaleToFit.CENTER);
-                    matrix.postRotate(rotation, mPhotoSize / 2f, mPhotoSize / 2f);
-                    canvas.drawBitmap(fullImage, matrix, new Paint());
-                    saveBitmapToFile(croppedImage, new File(mImagesDir, CROP_PICTURE_FILE_NAME));
-
-                    ThreadUtils.postOnMainThread(() -> {
-                        mAvatarUi.returnUriResult(mCropPictureUri);
-                    });
+            Matrix matrix = new Matrix();
+            RectF rectSource = new RectF(left, top,
+                    left + squareSize, top + squareSize);
+            RectF rectDest = new RectF(0, 0, mPhotoSize, mPhotoSize);
+            matrix.setRectToRect(rectSource, rectDest, Matrix.ScaleToFit.CENTER);
+            matrix.postRotate(rotation, mPhotoSize / 2f, mPhotoSize / 2f);
+            canvas.drawBitmap(fullImage, matrix, new Paint());
+            saveBitmapToFile(croppedImage, new File(mImagesDir, CROP_PICTURE_FILE_NAME));
+            return croppedImage;
+        });
+        Futures.addCallback(future, new FutureCallback<>() {
+            @Override
+            public void onSuccess(@Nullable Bitmap result) {
+                if (result != null) {
+                    mAvatarUi.returnUriResult(mCropPictureUri);
                 }
-            }).get();
-        } catch (InterruptedException | ExecutionException e) {
-            Log.e(TAG, "Error performing internal crop", e);
-        }
+            }
+
+            @Override
+            public void onFailure(Throwable t) {
+                Log.e(TAG, "Error performing internal crop", t);
+            }
+        }, mContextInjector.getContext().getMainExecutor());
     }
 
     /**
@@ -372,5 +392,10 @@
         public ContentResolver getContentResolver() {
             return mContext.getContentResolver();
         }
+
+        @Override
+        public Context getContext() {
+            return mContext;
+        }
     }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/CreateUserDialogController.java b/packages/SettingsLib/src/com/android/settingslib/users/CreateUserDialogController.java
index 8d03f70..53daef1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/CreateUserDialogController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/CreateUserDialogController.java
@@ -33,6 +33,7 @@
 import android.widget.RadioButton;
 import android.widget.RadioGroup;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
 
 import com.android.internal.util.UserIcons;
@@ -43,6 +44,10 @@
 import com.android.settingslib.utils.CustomDialogHelper;
 import com.android.settingslib.utils.ThreadUtils;
 
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+
 import java.io.File;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -291,12 +296,22 @@
 
     private void setUserIcon(Drawable defaultUserIcon, ImageView userPhotoView) {
         if (mCachedDrawablePath != null) {
-            ThreadUtils.postOnBackgroundThread(() -> {
-                mSavedPhoto = EditUserPhotoController.loadNewUserPhotoBitmap(
-                        new File(mCachedDrawablePath));
-                mSavedDrawable = CircleFramedDrawable.getInstance(mActivity, mSavedPhoto);
-                ThreadUtils.postOnMainThread(() -> userPhotoView.setImageDrawable(mSavedDrawable));
-            });
+            ListenableFuture<Drawable> future = ThreadUtils.getBackgroundExecutor()
+                    .submit(() -> {
+                        mSavedPhoto = EditUserPhotoController.loadNewUserPhotoBitmap(
+                                new File(mCachedDrawablePath));
+                        mSavedDrawable = CircleFramedDrawable.getInstance(mActivity, mSavedPhoto);
+                        return mSavedDrawable;
+                    });
+            Futures.addCallback(future, new FutureCallback<>() {
+                @Override
+                public void onSuccess(@NonNull Drawable result) {
+                    userPhotoView.setImageDrawable(result);
+                }
+
+                @Override
+                public void onFailure(Throwable t) {}
+            }, mActivity.getMainExecutor());
         } else {
             userPhotoView.setImageDrawable(defaultUserIcon);
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java b/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java
index 3fb2f60..9084aa2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java
@@ -26,17 +26,24 @@
 import android.util.Log;
 import android.widget.ImageView;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 import com.android.internal.util.UserIcons;
 import com.android.settingslib.drawable.CircleFramedDrawable;
 import com.android.settingslib.utils.ThreadUtils;
 
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.util.concurrent.ExecutionException;
 
 /**
  * This class contains logic for starting activities to take/choose/crop photo, reads and transforms
@@ -56,7 +63,7 @@
     private final ActivityStarter mActivityStarter;
     private final ImageView mImageView;
     private final String mFileAuthority;
-
+    private final ListeningExecutorService mExecutorService;
     private final File mImagesDir;
     private Bitmap mNewUserPhotoBitmap;
     private Drawable mNewUserPhotoDrawable;
@@ -75,6 +82,7 @@
 
         mNewUserPhotoBitmap = savedBitmap;
         mNewUserPhotoDrawable = savedDrawable;
+        mExecutorService = ThreadUtils.getBackgroundExecutor();
     }
 
     /**
@@ -113,22 +121,27 @@
     }
 
     private void onDefaultIconSelected(int tintColor) {
-        try {
-            ThreadUtils.postOnBackgroundThread(() -> {
-                Resources res = mActivity.getResources();
-                Drawable drawable =
-                        UserIcons.getDefaultUserIconInColor(res, tintColor);
-                Bitmap bitmap = UserIcons.convertToBitmapAtUserIconSize(res, drawable);
+        ListenableFuture<Bitmap> future = mExecutorService.submit(() -> {
+            Resources res = mActivity.getResources();
+            Drawable drawable =
+                    UserIcons.getDefaultUserIconInColor(res, tintColor);
+            return UserIcons.convertToBitmapAtUserIconSize(res, drawable);
+        });
+        Futures.addCallback(future, new FutureCallback<>() {
+            @Override
+            public void onSuccess(@NonNull Bitmap result) {
+                onPhotoProcessed(result);
+            }
 
-                ThreadUtils.postOnMainThread(() -> onPhotoProcessed(bitmap));
-            }).get();
-        } catch (InterruptedException | ExecutionException e) {
-            Log.e(TAG, "Error processing default icon", e);
-        }
+            @Override
+            public void onFailure(Throwable t) {
+                Log.e(TAG, "Error processing default icon", t);
+            }
+        }, mImageView.getContext().getMainExecutor());
     }
 
     private void onPhotoCropped(final Uri data) {
-        ThreadUtils.postOnBackgroundThread(() -> {
+        ListenableFuture<Bitmap> future = mExecutorService.submit(() -> {
             InputStream imageStream = null;
             Bitmap bitmap = null;
             try {
@@ -146,18 +159,23 @@
                     }
                 }
             }
-
-            if (bitmap != null) {
-                Bitmap finalBitmap = bitmap;
-                ThreadUtils.postOnMainThread(() -> onPhotoProcessed(finalBitmap));
-            }
+            return bitmap;
         });
+        Futures.addCallback(future, new FutureCallback<>() {
+            @Override
+            public void onSuccess(@Nullable Bitmap result) {
+                onPhotoProcessed(result);
+            }
+
+            @Override
+            public void onFailure(Throwable t) {}
+        }, mImageView.getContext().getMainExecutor());
     }
 
-    private void onPhotoProcessed(Bitmap bitmap) {
+    private void onPhotoProcessed(@Nullable Bitmap bitmap) {
         if (bitmap != null) {
             mNewUserPhotoBitmap = bitmap;
-            ThreadUtils.postOnBackgroundThread(() -> {
+            var unused = mExecutorService.submit(() -> {
                 mCachedDrawablePath = saveNewUserPhotoBitmap().getPath();
             });
             mNewUserPhotoDrawable = CircleFramedDrawable
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java b/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java
index 2c1d5da..48c1bcc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java
@@ -18,16 +18,20 @@
 import android.os.Handler;
 import android.os.Looper;
 
+import androidx.annotation.NonNull;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+
 import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
 
 public class ThreadUtils {
 
     private static volatile Thread sMainThread;
     private static volatile Handler sMainThreadHandler;
-    private static volatile ExecutorService sThreadExecutor;
+    private static volatile ListeningExecutorService sListeningService;
 
     /**
      * Returns true if the current thread is the UI thread.
@@ -42,6 +46,7 @@
     /**
      * Returns a shared UI thread handler.
      */
+    @NonNull
     public static Handler getUiThreadHandler() {
         if (sMainThreadHandler == null) {
             sMainThreadHandler = new Handler(Looper.getMainLooper());
@@ -62,40 +67,47 @@
     /**
      * Posts runnable in background using shared background thread pool.
      *
-     * @Return A future of the task that can be monitored for updates or cancelled.
+     * @return A future of the task that can be monitored for updates or cancelled.
      */
-    public static Future postOnBackgroundThread(Runnable runnable) {
-        return getThreadExecutor().submit(runnable);
+    @SuppressWarnings("rawtypes")
+    @NonNull
+    public static ListenableFuture postOnBackgroundThread(@NonNull Runnable runnable) {
+        return getBackgroundExecutor().submit(runnable);
     }
 
     /**
      * Posts callable in background using shared background thread pool.
      *
-     * @Return A future of the task that can be monitored for updates or cancelled.
+     * @return A future of the task that can be monitored for updates or cancelled.
      */
-    public static Future postOnBackgroundThread(Callable callable) {
-        return getThreadExecutor().submit(callable);
+    @NonNull
+    public static <T> ListenableFuture<T> postOnBackgroundThread(@NonNull Callable<T> callable) {
+        return getBackgroundExecutor().submit(callable);
     }
 
     /**
      * Posts the runnable on the main thread.
+     *
+     * @deprecated moving work to the main thread should be done via the main executor provided to
+     * {@link com.google.common.util.concurrent.FutureCallback} via
+     * {@link android.content.Context#getMainExecutor()} or by calling an SDK method such as
+     * {@link android.app.Activity#runOnUiThread(Runnable)} or
+     * {@link android.content.Context#getMainThreadHandler()} where appropriate.
      */
-    public static void postOnMainThread(Runnable runnable) {
+    @Deprecated
+    public static void postOnMainThread(@NonNull Runnable runnable) {
         getUiThreadHandler().post(runnable);
     }
 
     /**
-     * Posts the runnable on the main thread with a delay.
+     * Provides a shared {@link ListeningExecutorService} created using a fixed thread pool executor
      */
-    public static void postOnMainThreadDelayed(Runnable runnable, long delayMillis) {
-        getUiThreadHandler().postDelayed(runnable, delayMillis);
-    }
-
-    private static synchronized ExecutorService getThreadExecutor() {
-        if (sThreadExecutor == null) {
-            sThreadExecutor = Executors.newFixedThreadPool(
-                    Runtime.getRuntime().availableProcessors());
+    @NonNull
+    public static synchronized ListeningExecutorService getBackgroundExecutor() {
+        if (sListeningService == null) {
+            sListeningService = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(
+                    Runtime.getRuntime().availableProcessors()));
         }
-        return sThreadExecutor;
+        return sListeningService;
     }
 }
diff --git a/packages/SettingsLib/tests/robotests/Android.bp b/packages/SettingsLib/tests/robotests/Android.bp
index 2d875cf..732c336 100644
--- a/packages/SettingsLib/tests/robotests/Android.bp
+++ b/packages/SettingsLib/tests/robotests/Android.bp
@@ -49,6 +49,8 @@
         "androidx.fragment_fragment",
         "androidx.test.core",
         "androidx.core_core",
+        "flag-junit",
+        "settingslib_flags_lib",
         "testng", // TODO: remove once JUnit on Android provides assertThrows
     ],
     java_resource_dirs: ["config"],
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 85efe69..ed545ab 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -35,13 +35,18 @@
 import android.content.Context;
 import android.graphics.drawable.BitmapDrawable;
 import android.media.AudioManager;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.text.Spannable;
+import android.text.style.ForegroundColorSpan;
 import android.util.LruCache;
 
 import com.android.settingslib.R;
+import com.android.settingslib.media.flags.Flags;
 import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
 import com.android.settingslib.widget.AdaptiveOutlineDrawable;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -60,10 +65,13 @@
     private static final String DEVICE_ALIAS_NEW = "TestAliasNew";
     private static final String TWS_BATTERY_LEFT = "15";
     private static final String TWS_BATTERY_RIGHT = "25";
+    private static final String TWS_LOW_BATTERY_THRESHOLD_LOW = "10";
+    private static final String TWS_LOW_BATTERY_THRESHOLD_HIGH = "25";
     private static final short RSSI_1 = 10;
     private static final short RSSI_2 = 11;
     private static final boolean JUSTDISCOVERED_1 = true;
     private static final boolean JUSTDISCOVERED_2 = false;
+    private static final int LOW_BATTERY_COLOR = android.R.color.holo_red_dark;
     @Mock
     private LocalBluetoothProfileManager mProfileManager;
     @Mock
@@ -89,9 +97,13 @@
     private int mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
     private ShadowBluetoothAdapter mShadowBluetoothAdapter;
 
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+        mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TV_MEDIA_OUTPUT_DIALOG);
         mContext = RuntimeEnvironment.application;
         mAudioManager = mContext.getSystemService(AudioManager.class);
         mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
@@ -179,6 +191,17 @@
     }
 
     @Test
+    public void getTvConnectionSummary_testProfilesInactive_returnPairing() {
+        // Arrange:
+        //   Bond State: Bonding
+        when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDING);
+
+        // Act & Assert:
+        //   Get "Pairing…" result without Battery Level.
+        assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo("Pairing…");
+    }
+
+    @Test
     public void getConnectionSummary_testSingleProfileConnectDisconnect() {
         // Test without battery level
         // Set PAN profile to be connected and test connection state summary
@@ -212,6 +235,39 @@
     }
 
     @Test
+    public void getTvConnectionSummary_testSingleProfileConnectDisconnect() {
+        // Test without battery level
+        // Set PAN profile to be connected and test connection state summary
+        updateProfileStatus(mPanProfile, BluetoothProfile.STATE_CONNECTED);
+        assertThat(mCachedDevice.getTvConnectionSummary()).isNull();
+
+        // Set PAN profile to be disconnected and test connection state summary
+        updateProfileStatus(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
+        assertThat(mCachedDevice.getTvConnectionSummary()).isNull();
+
+        // Test with battery level
+        mBatteryLevel = 10;
+        // Set PAN profile to be connected and test connection state summary
+        updateProfileStatus(mPanProfile, BluetoothProfile.STATE_CONNECTED);
+        assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo("Battery 10%");
+
+        // Set PAN profile to be disconnected and test connection state summary
+        updateProfileStatus(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
+        assertThat(mCachedDevice.getTvConnectionSummary()).isNull();
+
+        // Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level
+        mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
+
+        // Set PAN profile to be connected and test connection state summary
+        updateProfileStatus(mPanProfile, BluetoothProfile.STATE_CONNECTED);
+        assertThat(mCachedDevice.getTvConnectionSummary()).isNull();
+
+        // Set PAN profile to be disconnected and test connection state summary
+        updateProfileStatus(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
+        assertThat(mCachedDevice.getTvConnectionSummary()).isNull();
+    }
+
+    @Test
     public void getConnectionSummary_testMultipleProfileConnectDisconnect() {
         mBatteryLevel = 10;
 
@@ -243,6 +299,37 @@
     }
 
     @Test
+    public void getTvConnectionSummary_testMultipleProfileConnectDisconnect() {
+        mBatteryLevel = 10;
+
+        // Set HFP, A2DP and PAN profile to be connected and test connection state summary
+        updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mPanProfile, BluetoothProfile.STATE_CONNECTED);
+        assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo("Battery 10%");
+
+        // Disconnect HFP only and test connection state summary
+        updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+        assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo(
+                "Battery 10%");
+
+        // Disconnect A2DP only and test connection state summary
+        updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+        assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo(
+                "Battery 10%");
+
+        // Disconnect both HFP and A2DP and test connection state summary
+        updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+        assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo(
+                "Battery 10%");
+
+        // Disconnect all profiles and test connection state summary
+        updateProfileStatus(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
+        assertThat(mCachedDevice.getTvConnectionSummary()).isNull();
+    }
+
+    @Test
     public void getConnectionSummary_testSingleProfileActiveDeviceA2dp() {
         // Test without battery level
         // Set A2DP profile to be connected and test connection state summary
@@ -275,6 +362,37 @@
     }
 
     @Test
+    public void getTvConnectionSummary_testSingleProfileActiveDeviceA2dp() {
+        // Test without battery level
+        // Set A2DP profile to be connected and test connection state summary
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        assertThat(mCachedDevice.getTvConnectionSummary()).isNull();
+
+        // Set device as Active for A2DP and test connection state summary
+        mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
+        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active");
+
+        // Test with battery level
+        mBatteryLevel = 10;
+        assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo("Battery 10%");
+
+        // Set A2DP profile to be disconnected and test connection state summary
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+        assertThat(mCachedDevice.getTvConnectionSummary()).isNull();
+
+        // Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level
+        mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
+        // Set A2DP profile to be connected, Active and test connection state summary
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
+        assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo("Active");
+
+        // Set A2DP profile to be disconnected and test connection state summary
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+        assertThat(mCachedDevice.getTvConnectionSummary()).isNull();
+    }
+
+    @Test
     public void getConnectionSummary_shortSummary_returnShortSummary() {
         // Test without battery level
         // Set A2DP profile to be connected and test connection state summary
@@ -309,6 +427,18 @@
     }
 
     @Test
+    public void getTvConnectionSummary_testA2dpBatteryInactive_returnBattery() {
+        // Arrange:
+        //   1. Profile:       {A2DP, CONNECTED, Inactive}
+        //   2. Battery Level: 10
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        mBatteryLevel = 10;
+
+        // Act & Assert:
+        assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo("Battery 10%");
+    }
+
+    @Test
     public void getConnectionSummary_testA2dpInCall_returnNull() {
         // Arrange:
         //   1. Profile:       {A2DP, Connected, Active}
@@ -323,6 +453,20 @@
     }
 
     @Test
+    public void getTvConnectionSummary_testA2dpInCall_returnNull() {
+        // Arrange:
+        //   1. Profile:       {A2DP, Connected, Active}
+        //   2. Audio Manager: In Call
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
+        mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+
+        // Act & Assert:
+        //   Get null result without Battery Level.
+        assertThat(mCachedDevice.getTvConnectionSummary()).isNull();
+    }
+
+    @Test
     public void getConnectionSummary_testA2dpBatteryInCall_returnBattery() {
         // Arrange:
         //   1. Profile:       {A2DP, Connected, Active}
@@ -339,6 +483,22 @@
     }
 
     @Test
+    public void getTvConnectionSummary_testA2dpBatteryInCall_returnBattery() {
+        // Arrange:
+        //   1. Profile:       {A2DP, Connected, Active}
+        //   3. Battery Level: 10
+        //   2. Audio Manager: In Call
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
+        mBatteryLevel = 10;
+        mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+
+        // Act & Assert:
+        //   Get "10% battery" result with Battery Level 10.
+        assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo("Battery 10%");
+    }
+
+    @Test
     public void getConnectionSummary_testSingleProfileActiveDeviceHfp() {
         // Test without battery level
         // Set HFP profile to be connected and test connection state summary
@@ -372,6 +532,39 @@
     }
 
     @Test
+    public void getTvConnectionSummary_testSingleProfileActiveDeviceHfp() {
+        // Test without battery level
+        // Set HFP profile to be connected and test connection state summary
+        updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+        assertThat(mCachedDevice.getTvConnectionSummary()).isNull();
+
+        // Set device as Active for HFP and test connection state summary
+        mCachedDevice.onAudioModeChanged();
+        mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+        mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
+        assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo("Active");
+
+        // Test with battery level
+        mBatteryLevel = 10;
+        assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo("Battery 10%");
+
+        // Set HFP profile to be disconnected and test connection state summary
+        updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+        assertThat(mCachedDevice.getTvConnectionSummary()).isNull();
+
+        // Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level
+        mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
+        // Set HFP profile to be connected, Active and test connection state summary
+        updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+        mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
+        assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo("Active");
+
+        // Set HFP profile to be disconnected and test connection state summary
+        updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+        assertThat(mCachedDevice.getTvConnectionSummary()).isNull();
+    }
+
+    @Test
     public void getConnectionSummary_testHeadsetBatteryInactive_returnBattery() {
         // Arrange:
         //   1. Profile:       {HEADSET, CONNECTED, Inactive}
@@ -385,6 +578,19 @@
     }
 
     @Test
+    public void getTvConnectionSummary_testHeadsetBatteryInactive_returnBattery() {
+        // Arrange:
+        //   1. Profile:       {HEADSET, CONNECTED, Inactive}
+        //   2. Battery Level: 10
+        updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+        mBatteryLevel = 10;
+
+        // Act & Assert:
+        //   Get "10% battery" result without Battery Level.
+        assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo("Battery 10%");
+    }
+
+    @Test
     public void getConnectionSummary_testHeadsetWithoutInCall_returnNull() {
         // Arrange:
         //   1. Profile:       {HEADSET, Connected, Active}
@@ -398,6 +604,19 @@
     }
 
     @Test
+    public void getTvConnectionSummary_testHeadsetWithoutInCall_returnNull() {
+        // Arrange:
+        //   1. Profile:       {HEADSET, Connected, Active}
+        //   2. Audio Manager: Normal (Without In Call)
+        updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+        mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
+
+        // Act & Assert:
+        //   Get null result without Battery Level.
+        assertThat(mCachedDevice.getTvConnectionSummary()).isNull();
+    }
+
+    @Test
     public void getConnectionSummary_testHeadsetBatteryWithoutInCall_returnBattery() {
         // Arrange:
         //   1. Profile:       {HEADSET, Connected, Active}
@@ -413,6 +632,22 @@
     }
 
     @Test
+    public void getTvConnectionSummary_testHeadsetBatteryWithoutInCall_returnBattery() {
+        // Arrange:
+        //   1. Profile:       {HEADSET, Connected, Active}
+        //   2. Battery Level: 10
+        //   3. Audio Manager: Normal (Without In Call)
+        updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+        mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
+        mBatteryLevel = 10;
+
+        // Act & Assert:
+        //   Get "10% battery" result with Battery Level 10.
+        assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo("Battery 10%");
+    }
+
+
+    @Test
     public void getConnectionSummary_testSingleProfileActiveDeviceHearingAid() {
         // Test without battery level
         // Set Hearing Aid profile to be connected and test connection state summary
@@ -432,6 +667,26 @@
     }
 
     @Test
+    public void getTvConnectionSummary_testSingleProfileActiveDeviceHearingAid() {
+        // Test without battery level
+        // Set Hearing Aid profile to be connected and test connection state summary
+        updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
+        assertThat(mCachedDevice.getTvConnectionSummary()).isNull();
+
+        // Set device as Active for Hearing Aid and test connection state summary
+        mCachedDevice.setHearingAidInfo(getLeftAshaHearingAidInfo());
+        mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEARING_AID);
+        assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo(
+                "Active, left only");
+
+        // Set Hearing Aid profile to be disconnected and test connection state summary
+        mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.HEARING_AID);
+        mCachedDevice.onProfileStateChanged(mHearingAidProfile,
+                BluetoothProfile.STATE_DISCONNECTED);
+        assertThat(mCachedDevice.getTvConnectionSummary()).isNull();
+    }
+
+    @Test
     public void getConnectionSummary_testHearingAidBatteryInactive_returnBattery() {
         // Arrange:
         //   1. Profile:       {HEARING_AID, CONNECTED, Inactive}
@@ -445,6 +700,19 @@
     }
 
     @Test
+    public void getTvConnectionSummary_testHearingAidBatteryInactive_returnBattery() {
+        // Arrange:
+        //   1. Profile:       {HEARING_AID, CONNECTED, Inactive}
+        //   2. Battery Level: 10
+        updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
+        mBatteryLevel = 10;
+
+        // Act & Assert:
+        //   Get "10% battery" result without Battery Level.
+        assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo("Battery 10%");
+    }
+
+    @Test
     public void getConnectionSummary_testHearingAidBatteryWithoutInCall_returnActiveBattery() {
         // Arrange:
         //   1. Profile:       {HEARING_AID, Connected, Active}
@@ -460,6 +728,21 @@
     }
 
     @Test
+    public void getTvConnectionSummary_testHearingAidBatteryWithoutInCall_returnBattery() {
+        // Arrange:
+        //   1. Profile:       {HEARING_AID, Connected, Active}
+        //   2. Battery Level: 10
+        //   3. Audio Manager: Normal (Without In Call)
+        updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
+        mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEARING_AID);
+        mBatteryLevel = 10;
+
+        // Act & Assert:
+        //   Get "Active, 10% battery" result with Battery Level 10.
+        assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo("Battery 10%");
+    }
+
+    @Test
     public void getConnectionSummary_testHearingAidRightEarInCall_returnActiveRightEar() {
         // Arrange:
         //   1. Profile:       {HEARING_AID, Connected, Active, Right ear}
@@ -475,6 +758,22 @@
     }
 
     @Test
+    public void getTvConnectionSummary_testHearingAidRightEarInCall_returnActiveRightEar() {
+        // Arrange:
+        //   1. Profile:       {HEARING_AID, Connected, Active, Right ear}
+        //   2. Audio Manager: In Call
+        updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
+        mCachedDevice.setHearingAidInfo(getRightAshaHearingAidInfo());
+        mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEARING_AID);
+        mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+
+        // Act & Assert:
+        //   Get "Active" result without Battery Level.
+        assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo(
+                "Active, right only");
+    }
+
+    @Test
     public void getConnectionSummary_testHearingAidBothEarInCall_returnActiveBothEar() {
         // Arrange:
         //   1. Profile:       {HEARING_AID, Connected, Active, Both ear}
@@ -493,6 +792,25 @@
     }
 
     @Test
+    public void getTvConnectionSummary_testHearingAidBothEarInCall_returnActiveBothEar() {
+        // Arrange:
+        //   1. Profile:       {HEARING_AID, Connected, Active, Both ear}
+        //   2. Audio Manager: In Call
+        mCachedDevice.setHearingAidInfo(getRightAshaHearingAidInfo());
+        updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
+        mSubCachedDevice.setHearingAidInfo(getLeftAshaHearingAidInfo());
+        updateSubDeviceProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
+        mCachedDevice.setSubDevice(mSubCachedDevice);
+        mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEARING_AID);
+        mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+
+        // Act & Assert:
+        //   Get "Active" result without Battery Level.
+        assertThat(mCachedDevice.getTvConnectionSummary().toString())
+                .isEqualTo("Active, left and right");
+    }
+
+    @Test
     public void getConnectionSummary_testHearingAidBatteryInCall_returnActiveBattery() {
         // Arrange:
         //   1. Profile:       {HEARING_AID, Connected, Active}
@@ -509,6 +827,22 @@
     }
 
     @Test
+    public void getTvConnectionSummary_testHearingAidBatteryInCall_returnBattery() {
+        // Arrange:
+        //   1. Profile:       {HEARING_AID, Connected, Active}
+        //   2. Battery Level: 10
+        //   3. Audio Manager: In Call
+        updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
+        mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEARING_AID);
+        mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+        mBatteryLevel = 10;
+
+        // Act & Assert:
+        //   Get "Active, 10% battery" result with Battery Level 10.
+        assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo("Battery 10%");
+    }
+
+    @Test
     public void getConnectionSummary_testActiveDeviceLeAudioHearingAid() {
         // Test without battery level
         // Set HAP Client and LE Audio profile to be connected and test connection state summary
@@ -529,6 +863,27 @@
     }
 
     @Test
+    public void getTvConnectionSummary_testActiveDeviceLeAudioHearingAid() {
+        // Test without battery level
+        // Set HAP Client and LE Audio profile to be connected and test connection state summary
+        when(mProfileManager.getHapClientProfile()).thenReturn(mHapClientProfile);
+        updateProfileStatus(mHapClientProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mLeAudioProfile, BluetoothProfile.STATE_CONNECTED);
+        assertThat(mCachedDevice.getTvConnectionSummary()).isNull();
+
+        // Set device as Active for LE Audio and test connection state summary
+        mCachedDevice.setHearingAidInfo(getLeftLeAudioHearingAidInfo());
+        mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.LE_AUDIO);
+        assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo(
+                "Active, left only");
+
+        // Set LE Audio profile to be disconnected and test connection state summary
+        mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.LE_AUDIO);
+        mCachedDevice.onProfileStateChanged(mLeAudioProfile, BluetoothProfile.STATE_DISCONNECTED);
+        assertThat(mCachedDevice.getTvConnectionSummary()).isNull();
+    }
+
+    @Test
     public void getConnectionSummary_testMemberDevicesExist_returnMinBattery() {
         // One device is active with battery level 70.
         mBatteryLevel = 70;
@@ -545,6 +900,22 @@
     }
 
     @Test
+    public void getTvConnectionSummary_testMemberDevicesExist_returnMinBattery() {
+        // One device is active with battery level 70.
+        mBatteryLevel = 70;
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
+
+
+        // Add a member device with battery level 30.
+        int lowerBatteryLevel = 30;
+        mCachedDevice.addMemberDevice(mSubCachedDevice);
+        doAnswer((invocation) -> lowerBatteryLevel).when(mSubCachedDevice).getBatteryLevel();
+
+        assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo("Battery 30%");
+    }
+
+    @Test
     public void getConnectionSummary_testMemberDevicesBatteryUnknown_returnMinBattery() {
         // One device is active with battery level 70.
         mBatteryLevel = 70;
@@ -560,6 +931,21 @@
     }
 
     @Test
+    public void getTvConnectionSummary_testMemberDevicesBatteryUnknown_returnMinBattery() {
+        // One device is active with battery level 70.
+        mBatteryLevel = 70;
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
+
+        // Add a member device with battery level unknown.
+        mCachedDevice.addMemberDevice(mSubCachedDevice);
+        doAnswer((invocation) -> BluetoothDevice.BATTERY_LEVEL_UNKNOWN).when(
+                mSubCachedDevice).getBatteryLevel();
+
+        assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo("Battery 70%");
+    }
+
+    @Test
     public void getConnectionSummary_testAllDevicesBatteryUnknown_returnNoBattery() {
         // One device is active with battery level unknown.
         updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
@@ -574,6 +960,20 @@
     }
 
     @Test
+    public void getTvConnectionSummary_testAllDevicesBatteryUnknown_returnNoBattery() {
+        // One device is active with battery level unknown.
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
+
+        // Add a member device with battery level unknown.
+        mCachedDevice.addMemberDevice(mSubCachedDevice);
+        doAnswer((invocation) -> BluetoothDevice.BATTERY_LEVEL_UNKNOWN).when(
+                mSubCachedDevice).getBatteryLevel();
+
+        assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo("Active");
+    }
+
+    @Test
     public void getConnectionSummary_testMultipleProfilesActiveDevice() {
         // Test without battery level
         // Set A2DP and HFP profiles to be connected and test connection state summary
@@ -621,6 +1021,53 @@
     }
 
     @Test
+    public void getTvConnectionSummary_testMultipleProfilesActiveDevice() {
+        // Test without battery level
+        // Set A2DP and HFP profiles to be connected and test connection state summary
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+        assertThat(mCachedDevice.getTvConnectionSummary()).isNull();
+
+        // Set device as Active for A2DP and HFP and test connection state summary
+        mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
+        mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
+        assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo("Active");
+
+        // Test with battery level
+        mBatteryLevel = 10;
+        assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo(
+                "Battery 10%");
+
+        // Disconnect A2DP only and test connection state summary
+        mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.A2DP);
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+        assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo(
+                "Battery 10%");
+
+        // Disconnect HFP only and test connection state summary
+        mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.HEADSET);
+        updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
+        assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo(
+                "Battery 10%");
+
+        // Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level
+        mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
+        // Set A2DP and HFP profiles to be connected, Active and test connection state summary
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+        mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
+        mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
+        assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo("Active");
+
+        // Set A2DP and HFP profiles to be disconnected and test connection state summary
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+        updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+        assertThat(mCachedDevice.getTvConnectionSummary()).isNull();
+    }
+
+    @Test
     public void getConnectionSummary_testMultipleProfilesInactive_returnPairing() {
         // Arrange:
         //   1. Profile 1:  {A2DP, CONNECTED, Inactive}
@@ -638,6 +1085,23 @@
     }
 
     @Test
+    public void getTvConnectionSummary_testMultipleProfilesInactive_returnPairing() {
+        // Arrange:
+        //   1. Profile 1:  {A2DP, CONNECTED, Inactive}
+        //   2. Profile 2:  {HEADSET, CONNECTED, Inactive}
+        //   3. Profile 3:  {HEARING_AID, CONNECTED, Inactive}
+        //   4. Bond State: Bonding
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
+        when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDING);
+
+        // Act & Assert:
+        //    Get "Pairing…" result without Battery Level.
+        assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo("Pairing…");
+    }
+
+    @Test
     public void getConnectionSummary_trueWirelessActiveDeviceWithBattery_returnActiveWithBattery() {
         updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
         updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
@@ -656,6 +1120,24 @@
     }
 
     @Test
+    public void getTvConnectionSummary_trueWirelessActiveDeviceWithBattery_returnBattery() {
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
+        when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+        mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEARING_AID);
+        when(mDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn(
+                "true".getBytes());
+        when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY)).thenReturn(
+                TWS_BATTERY_LEFT.getBytes());
+        when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTETHERED_RIGHT_BATTERY)).thenReturn(
+                TWS_BATTERY_RIGHT.getBytes());
+
+        assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo(
+                "Left 15% Right 25%");
+    }
+
+    @Test
     public void getConnectionSummary_trueWirelessDeviceWithBattery_returnActiveWithBattery() {
         updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
         updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
@@ -673,6 +1155,84 @@
     }
 
     @Test
+    public void getTvConnectionSummary_trueWirelessDeviceWithBattery_returnBattery() {
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
+        when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+        when(mDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn(
+                "true".getBytes());
+        when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY)).thenReturn(
+                TWS_BATTERY_LEFT.getBytes());
+        when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTETHERED_RIGHT_BATTERY)).thenReturn(
+                TWS_BATTERY_RIGHT.getBytes());
+
+        assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo(
+                "Left 15% Right 25%");
+    }
+
+    @Test
+    public void getTvConnectionSummary_trueWirelessDeviceWithLowBattery() {
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
+        when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+        when(mDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn(
+                "true".getBytes());
+        when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY)).thenReturn(
+                TWS_BATTERY_LEFT.getBytes());
+        when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTETHERED_RIGHT_BATTERY)).thenReturn(
+                TWS_BATTERY_RIGHT.getBytes());
+
+        int lowBatteryColor = mContext.getColor(LOW_BATTERY_COLOR);
+
+        // Default low battery threshold, only left battery is low
+        CharSequence summary = mCachedDevice.getTvConnectionSummary(LOW_BATTERY_COLOR);
+        assertForegroundColorSpan(summary, 0, 0, 8, lowBatteryColor);
+        assertThat(summary.toString()).isEqualTo("Left 15% Right 25%");
+
+        // Lower threshold, neither battery should be low
+        when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTETHERED_LEFT_LOW_BATTERY_THRESHOLD))
+                .thenReturn(TWS_LOW_BATTERY_THRESHOLD_LOW.getBytes());
+        when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTETHERED_RIGHT_LOW_BATTERY_THRESHOLD))
+                .thenReturn(TWS_LOW_BATTERY_THRESHOLD_LOW.getBytes());
+        summary = mCachedDevice.getTvConnectionSummary(LOW_BATTERY_COLOR);
+        assertNoForegroundColorSpans(summary);
+        assertThat(summary.toString()).isEqualTo("Left 15% Right 25%");
+
+
+        // Higher Threshold, both batteries are low
+        when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTETHERED_LEFT_LOW_BATTERY_THRESHOLD))
+                .thenReturn(TWS_LOW_BATTERY_THRESHOLD_HIGH.getBytes());
+        when(mDevice.getMetadata(BluetoothDevice.METADATA_UNTETHERED_RIGHT_LOW_BATTERY_THRESHOLD))
+                .thenReturn(TWS_LOW_BATTERY_THRESHOLD_HIGH.getBytes());
+        summary = mCachedDevice.getTvConnectionSummary(LOW_BATTERY_COLOR);
+        assertForegroundColorSpan(summary, 0, 0, 8, lowBatteryColor);
+        assertForegroundColorSpan(summary, 1, 9, 18, lowBatteryColor);
+        assertThat(summary.toString()).isEqualTo("Left 15% Right 25%");
+    }
+
+    private void assertNoForegroundColorSpans(CharSequence charSequence) {
+        if (charSequence instanceof Spannable) {
+            Spannable summarySpan = (Spannable) charSequence;
+            ForegroundColorSpan[] spans = summarySpan.getSpans(0, summarySpan.length(),
+                    ForegroundColorSpan.class);
+            assertThat(spans).isEmpty();
+        }
+    }
+
+    private void assertForegroundColorSpan(CharSequence charSequence, int indexInSpannable,
+            int start, int end, int color) {
+        assertThat(charSequence).isInstanceOf(Spannable.class);
+        Spannable summarySpan = (Spannable) charSequence;
+        ForegroundColorSpan[] spans = summarySpan.getSpans(0, summarySpan.length(),
+                ForegroundColorSpan.class);
+        assertThat(spans[indexInSpannable].getForegroundColor()).isEqualTo(color);
+        assertThat(summarySpan.getSpanStart(spans[indexInSpannable])).isEqualTo(start);
+        assertThat(summarySpan.getSpanEnd(spans[indexInSpannable])).isEqualTo(end);
+    }
+
+    @Test
     public void getCarConnectionSummary_singleProfileConnectDisconnect() {
         // Test without battery level
         // Set PAN profile to be connected and test connection state summary
@@ -1136,6 +1696,18 @@
     }
 
     @Test
+    public void getTvConnectionSummary_profileConnectedFail_showErrorMessage() {
+        final A2dpProfile profile = mock(A2dpProfile.class);
+        mCachedDevice.onProfileStateChanged(profile, BluetoothProfile.STATE_CONNECTED);
+        mCachedDevice.setProfileConnectedStatus(BluetoothProfile.A2DP, true);
+
+        when(profile.getConnectionStatus(mDevice)).thenReturn(BluetoothProfile.STATE_CONNECTED);
+
+        assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo(
+                mContext.getString(R.string.profile_connect_timeout_subtext));
+    }
+
+    @Test
     public void onUuidChanged_bluetoothClassIsNull_shouldNotCrash() {
         mShadowBluetoothAdapter.setUuids(PbapServerProfile.PBAB_CLIENT_UUIDS);
         when(mDevice.getUuids()).thenReturn(PbapServerProfile.PBAB_CLIENT_UUIDS);
diff --git a/packages/SettingsProvider/Android.bp b/packages/SettingsProvider/Android.bp
index a4a9290..adebdcd 100644
--- a/packages/SettingsProvider/Android.bp
+++ b/packages/SettingsProvider/Android.bp
@@ -33,6 +33,7 @@
     ],
     static_libs: [
         "device_config_service_flags_java",
+        "libaconfig_java_proto_lite",
         "SettingsLibDeviceStateRotationLock",
         "SettingsLibDisplayUtils",
     ],
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
index 969f1fd..976ba21 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
@@ -22,6 +22,8 @@
 
 import static com.android.providers.settings.Flags.supportOverrides;
 
+import android.aconfig.Aconfig.parsed_flag;
+import android.aconfig.Aconfig.parsed_flags;
 import android.annotation.SuppressLint;
 import android.app.ActivityManager;
 import android.content.AttributionSource;
@@ -39,12 +41,13 @@
 import android.provider.Settings;
 import android.provider.Settings.Config.SyncDisabledMode;
 import android.provider.UpdatableDeviceConfigServiceReadiness;
+import android.util.Slog;
 
 import com.android.internal.util.FastPrintWriter;
 
 import java.io.File;
 import java.io.FileDescriptor;
-import java.io.FileNotFoundException;
+import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
@@ -56,18 +59,17 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Scanner;
 
 /**
  * Receives shell commands from the command line related to device config flags, and dispatches them
  * to the SettingsProvider.
  */
 public final class DeviceConfigService extends Binder {
-    private static final List<String> aconfigTextProtoFilesOnDevice = List.of(
-        "/system/etc/aconfig_flags.textproto",
-        "/system_ext/etc/aconfig_flags.textproto",
-        "/system_ext/etc/aconfig_flags.textproto",
-        "/vendor/etc/aconfig_flags.textproto");
+    private static final List<String> sAconfigTextProtoFilesOnDevice = List.of(
+            "/system/etc/aconfig_flags.pb",
+            "/system_ext/etc/aconfig_flags.pb",
+            "/system_ext/etc/aconfig_flags.pb",
+            "/vendor/etc/aconfig_flags.pb");
 
     private static final List<String> PRIVATE_NAMESPACES = List.of(
             "device_config_overrides",
@@ -76,6 +78,8 @@
 
     final SettingsProvider mProvider;
 
+    private static final String TAG = "DeviceConfigService";
+
     public DeviceConfigService(SettingsProvider provider) {
         mProvider = provider;
     }
@@ -94,62 +98,55 @@
 
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-      final IContentProvider iprovider = mProvider.getIContentProvider();
-      pw.println("DeviceConfig flags:");
-      for (String line : MyShellCommand.listAll(iprovider)) {
-        pw.println(line);
-      }
+        final IContentProvider iprovider = mProvider.getIContentProvider();
+        pw.println("DeviceConfig flags:");
+        for (String line : MyShellCommand.listAll(iprovider)) {
+            pw.println(line);
+        }
 
-      ArrayList<String> missingFiles = new ArrayList<String>();
-      for (String fileName : aconfigTextProtoFilesOnDevice) {
-        File aconfigFile = new File(fileName);
-        if (!aconfigFile.exists()) {
-          missingFiles.add(fileName);
+        ArrayList<String> missingFiles = new ArrayList<String>();
+        for (String fileName : sAconfigTextProtoFilesOnDevice) {
+            File aconfigFile = new File(fileName);
+            if (!aconfigFile.exists()) {
+                missingFiles.add(fileName);
+            }
         }
-      }
 
-      if (missingFiles.isEmpty()) {
-        pw.println("\nAconfig flags:");
-        for (String name : MyShellCommand.listAllAconfigFlags(iprovider)) {
-          pw.println(name);
+        if (missingFiles.isEmpty()) {
+            pw.println("\nAconfig flags:");
+            for (String name : MyShellCommand.listAllAconfigFlags(iprovider)) {
+                pw.println(name);
+            }
+        } else {
+            pw.println("\nFailed to dump aconfig flags due to missing files:");
+            for (String fileName : missingFiles) {
+                pw.println(fileName);
+            }
         }
-      } else {
-        pw.println("\nFailed to dump aconfig flags due to missing files:");
-        for (String fileName : missingFiles) {
-          pw.println(fileName);
-        }
-      }
     }
 
     private static HashSet<String> getAconfigFlagNamesInDeviceConfig() {
         HashSet<String> nameSet = new HashSet<String>();
-        for (String fileName : aconfigTextProtoFilesOnDevice) {
-          try{
-            File aconfigFile = new File(fileName);
-            String packageName = "";
-            String namespace = "";
-            String name = "";
-
-            try (Scanner scanner = new Scanner(aconfigFile)) {
-              while (scanner.hasNextLine()) {
-                String data = scanner.nextLine().replaceAll("\\s+","");
-                if (data.startsWith("package:\"")) {
-                  packageName = data.substring(9, data.length()-1);
-                } else if (data.startsWith("name:\"")) {
-                  name = data.substring(6, data.length()-1);
-                } else if (data.startsWith("namespace:\"")) {
-                  namespace = data.substring(11, data.length()-1);
-                  nameSet.add(namespace + "/" + packageName + "." + name);
+        try {
+            for (String fileName : sAconfigTextProtoFilesOnDevice) {
+                byte[] contents = (new FileInputStream(fileName)).readAllBytes();
+                parsed_flags parsedFlags = parsed_flags.parseFrom(contents);
+                if (parsedFlags == null) {
+                    Slog.e(TAG, "failed to parse aconfig protobuf from " + fileName);
+                    continue;
                 }
-              }
+
+                for (parsed_flag flag : parsedFlags.getParsedFlagList()) {
+                    String namespace = flag.getNamespace();
+                    String packageName = flag.getPackage();
+                    String name = flag.getName();
+                    nameSet.add(namespace + "/" + packageName + "." + name);
+                }
             }
-
-          } catch (FileNotFoundException e) {
-            continue;
-          }
+        } catch (IOException e) {
+            Slog.e(TAG, "failed to read aconfig protobuf", e);
         }
-
-      return nameSet;
+        return nameSet;
     }
 
     private void callUpdableDeviceConfigShellCommandHandler(FileDescriptor in, FileDescriptor out,
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 17cc9f8..f78811f 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -60,7 +60,6 @@
             // except for SystemUI-core.
             // Copied from compose/features/Android.bp.
             static_libs: [
-                "CommunalLayoutLib",
                 "PlatformComposeCore",
                 "PlatformComposeSceneTransitionLayout",
 
diff --git a/packages/SystemUI/aconfig/accessibility.aconfig b/packages/SystemUI/aconfig/accessibility.aconfig
index 25ac486..21263a9 100644
--- a/packages/SystemUI/aconfig/accessibility.aconfig
+++ b/packages/SystemUI/aconfig/accessibility.aconfig
@@ -6,7 +6,7 @@
     name: "floating_menu_animated_tuck"
     namespace: "accessibility"
     description: "Sets up animations for tucking/untucking and adjusts clipbounds."
-    bug: "24592044"
+    bug: "297556899"
 }
 
 flag {
@@ -21,4 +21,11 @@
     namespace: "accessibility"
     description: "Adjusts bounds to allow the floating menu to render on top of navigation bars."
     bug: "283768342"
+}
+
+flag {
+    name: "floating_menu_radii_animation"
+    namespace: "accessibility"
+    description: "Animates the floating menu's transition between curved and jagged edges."
+    bug: "281140482"
 }
\ No newline at end of file
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index c26d5f5..8eff9bf 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -51,3 +51,11 @@
     description: "Enables the scene container framework go/flexiglass."
     bug: "283121968"
 }
+
+flag {
+    name: "visual_interruptions_refactor"
+    namespace: "systemui"
+    description: "Enables the refactored version of the code to decide when notifications "
+        "HUN, bubble, pulse, or FSI."
+    bug: "261728888"
+}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
index ab4db45..f7d9056c3 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -114,7 +114,13 @@
         private val NAV_FADE_OUT_INTERPOLATOR = PathInterpolator(0.2f, 0f, 1f, 1f)
 
         /** The time we wait before timing out the remote animation after starting the intent. */
-        private const val LAUNCH_TIMEOUT = 1000L
+        private const val LAUNCH_TIMEOUT = 1_000L
+
+        /**
+         * The time we wait before we Log.wtf because the remote animation was neither started or
+         * cancelled by WM.
+         */
+        private const val LONG_LAUNCH_TIMEOUT = 5_000L
 
         private fun createPositionXInterpolator(): Interpolator {
             val path =
@@ -247,7 +253,7 @@
         // If we expect an animation, post a timeout to cancel it in case the remote animation is
         // never started.
         if (willAnimate) {
-            runnerDelegate.postTimeout()
+            runnerDelegate.postTimeouts()
 
             // Hide the keyguard using the launch animation instead of the default unlock animation.
             if (hideKeyguardWithAnimation) {
@@ -578,21 +584,41 @@
         private var cancelled = false
         private var animation: LaunchAnimator.Animation? = null
 
-        // A timeout to cancel the remote animation if it is not started within X milliseconds after
-        // the intent was started.
-        //
-        // Note that this is important to keep this a Runnable (and not a Kotlin lambda), otherwise
-        // it will be automatically converted when posted and we wouldn't be able to remove it after
-        // posting it.
+        /**
+         * A timeout to cancel the launch animation if the remote animation is not started or
+         * cancelled within [LAUNCH_TIMEOUT] milliseconds after the intent was started.
+         *
+         * Note that this is important to keep this a Runnable (and not a Kotlin lambda), otherwise
+         * it will be automatically converted when posted and we wouldn't be able to remove it after
+         * posting it.
+         */
         private var onTimeout = Runnable { onAnimationTimedOut() }
 
-        @UiThread
-        internal fun postTimeout() {
-            timeoutHandler?.postDelayed(onTimeout, LAUNCH_TIMEOUT)
+        /**
+         * A long timeout to Log.wtf (signaling a bug in WM) when the remote animation wasn't
+         * started or cancelled within [LONG_LAUNCH_TIMEOUT] milliseconds after the intent was
+         * started.
+         */
+        private var onLongTimeout = Runnable {
+            Log.wtf(
+                TAG,
+                "The remote animation was neither cancelled or started within $LONG_LAUNCH_TIMEOUT"
+            )
         }
 
-        private fun removeTimeout() {
-            timeoutHandler?.removeCallbacks(onTimeout)
+        @UiThread
+        internal fun postTimeouts() {
+            if (timeoutHandler != null) {
+                timeoutHandler.postDelayed(onTimeout, LAUNCH_TIMEOUT)
+                timeoutHandler.postDelayed(onLongTimeout, LONG_LAUNCH_TIMEOUT)
+            }
+        }
+
+        private fun removeTimeouts() {
+            if (timeoutHandler != null) {
+                timeoutHandler.removeCallbacks(onTimeout)
+                timeoutHandler.removeCallbacks(onLongTimeout)
+            }
         }
 
         @UiThread
@@ -603,7 +629,7 @@
             nonApps: Array<out RemoteAnimationTarget>?,
             callback: IRemoteAnimationFinishedCallback?
         ) {
-            removeTimeout()
+            removeTimeouts()
 
             // The animation was started too late and we already notified the controller that it
             // timed out.
@@ -653,7 +679,6 @@
             val window = findRootTaskIfPossible(apps)
             if (window == null) {
                 Log.i(TAG, "Aborting the animation as no window is opening")
-                removeTimeout()
                 iCallback?.invoke()
 
                 if (DEBUG_LAUNCH_ANIMATION) {
@@ -890,11 +915,13 @@
         }
 
         private fun onAnimationTimedOut() {
+            // The remote animation was cancelled by WM, so we already cancelled the launch
+            // animation.
             if (cancelled) {
                 return
             }
 
-            Log.wtf(TAG, "Remote animation timed out")
+            Log.w(TAG, "Remote animation timed out")
             timedOut = true
 
             if (DEBUG_LAUNCH_ANIMATION) {
@@ -906,13 +933,15 @@
 
         @UiThread
         override fun onAnimationCancelled() {
+            removeTimeouts()
+
+            // The short timeout happened, so we already cancelled the launch animation.
             if (timedOut) {
                 return
             }
 
             Log.i(TAG, "Remote animation was cancelled")
             cancelled = true
-            removeTimeout()
 
             animation?.cancel()
 
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index b8fb264..87a8c35 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -4,8 +4,14 @@
 import android.os.Bundle
 import android.util.SizeF
 import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.lazy.grid.GridCells
+import androidx.compose.foundation.lazy.grid.GridItemSpan
+import androidx.compose.foundation.lazy.grid.LazyHorizontalGrid
 import androidx.compose.material3.Card
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.collectAsState
@@ -13,16 +19,12 @@
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.res.dimensionResource
-import androidx.compose.ui.res.integerResource
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
 import androidx.compose.ui.viewinterop.AndroidView
-import com.android.systemui.communal.layout.ui.compose.CommunalGridLayout
-import com.android.systemui.communal.layout.ui.compose.config.CommunalGridLayoutCard
-import com.android.systemui.communal.layout.ui.compose.config.CommunalGridLayoutConfig
 import com.android.systemui.communal.shared.model.CommunalContentSize
 import com.android.systemui.communal.ui.model.CommunalContentUiModel
 import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
-import com.android.systemui.res.R
 
 @Composable
 fun CommunalHub(
@@ -34,68 +36,91 @@
     Box(
         modifier = modifier.fillMaxSize().background(Color.White),
     ) {
-        CommunalGridLayout(
-            modifier = Modifier.align(Alignment.CenterStart),
-            layoutConfig =
-                CommunalGridLayoutConfig(
-                    gridColumnSize = dimensionResource(R.dimen.communal_grid_column_size),
-                    gridGutter = dimensionResource(R.dimen.communal_grid_gutter_size),
-                    gridHeight = dimensionResource(R.dimen.communal_grid_height),
-                    gridColumnsPerCard = integerResource(R.integer.communal_grid_columns_per_card),
-                ),
-            communalCards = if (showTutorial) tutorialContent else widgetContent.map(::contentCard),
-        )
+        LazyHorizontalGrid(
+            modifier = modifier.height(Dimensions.GridHeight).align(Alignment.CenterStart),
+            rows = GridCells.Fixed(CommunalContentSize.FULL.span),
+            horizontalArrangement = Arrangement.spacedBy(Dimensions.Spacing),
+            verticalArrangement = Arrangement.spacedBy(Dimensions.Spacing),
+        ) {
+            if (showTutorial) {
+                items(
+                    count = tutorialContentSizes.size,
+                    // TODO(b/308148193): a more scalable solution for unique ids.
+                    key = { index -> "tutorial_$index" },
+                    span = { index -> GridItemSpan(tutorialContentSizes[index].span) },
+                ) { index ->
+                    TutorialCard(
+                        modifier =
+                            Modifier.size(Dimensions.CardWidth, tutorialContentSizes[index].dp()),
+                    )
+                }
+            } else {
+                items(
+                    count = widgetContent.size,
+                    key = { index -> widgetContent[index].id },
+                    span = { index -> GridItemSpan(widgetContent[index].size.span) },
+                ) { index ->
+                    val widget = widgetContent[index]
+                    ContentCard(
+                        modifier = Modifier.size(Dimensions.CardWidth, widget.size.dp()),
+                        model = widget,
+                    )
+                }
+            }
+        }
     }
 }
 
-private val tutorialContent =
+// A placeholder for tutorial content.
+@Composable
+private fun TutorialCard(modifier: Modifier = Modifier) {
+    Card(modifier = modifier, content = {})
+}
+
+@Composable
+private fun ContentCard(
+    model: CommunalContentUiModel,
+    modifier: Modifier = Modifier,
+) {
+    AndroidView(
+        modifier = modifier,
+        factory = {
+            model.view.apply {
+                if (this is AppWidgetHostView) {
+                    val size = SizeF(Dimensions.CardWidth.value, model.size.dp().value)
+                    updateAppWidgetSize(Bundle.EMPTY, listOf(size))
+                }
+            }
+        },
+    )
+}
+
+private fun CommunalContentSize.dp(): Dp {
+    return when (this) {
+        CommunalContentSize.FULL -> Dimensions.CardHeightFull
+        CommunalContentSize.HALF -> Dimensions.CardHeightHalf
+        CommunalContentSize.THIRD -> Dimensions.CardHeightThird
+    }
+}
+
+// Sizes for the tutorial placeholders.
+private val tutorialContentSizes =
     listOf(
-        tutorialCard(CommunalGridLayoutCard.Size.FULL),
-        tutorialCard(CommunalGridLayoutCard.Size.THIRD),
-        tutorialCard(CommunalGridLayoutCard.Size.THIRD),
-        tutorialCard(CommunalGridLayoutCard.Size.THIRD),
-        tutorialCard(CommunalGridLayoutCard.Size.HALF),
-        tutorialCard(CommunalGridLayoutCard.Size.HALF),
-        tutorialCard(CommunalGridLayoutCard.Size.HALF),
-        tutorialCard(CommunalGridLayoutCard.Size.HALF),
+        CommunalContentSize.FULL,
+        CommunalContentSize.THIRD,
+        CommunalContentSize.THIRD,
+        CommunalContentSize.THIRD,
+        CommunalContentSize.HALF,
+        CommunalContentSize.HALF,
+        CommunalContentSize.HALF,
+        CommunalContentSize.HALF,
     )
 
-private fun tutorialCard(size: CommunalGridLayoutCard.Size): CommunalGridLayoutCard {
-    return object : CommunalGridLayoutCard() {
-        override val supportedSizes = listOf(size)
-
-        @Composable
-        override fun Content(modifier: Modifier, size: SizeF) {
-            Card(modifier = modifier, content = {})
-        }
-    }
-}
-
-private fun contentCard(model: CommunalContentUiModel): CommunalGridLayoutCard {
-    return object : CommunalGridLayoutCard() {
-        override val supportedSizes = listOf(convertToCardSize(model.size))
-        override val priority = model.priority
-
-        @Composable
-        override fun Content(modifier: Modifier, size: SizeF) {
-            AndroidView(
-                modifier = modifier,
-                factory = {
-                    model.view.apply {
-                        if (this is AppWidgetHostView) {
-                            updateAppWidgetSize(Bundle(), listOf(size))
-                        }
-                    }
-                },
-            )
-        }
-    }
-}
-
-private fun convertToCardSize(size: CommunalContentSize): CommunalGridLayoutCard.Size {
-    return when (size) {
-        CommunalContentSize.FULL -> CommunalGridLayoutCard.Size.FULL
-        CommunalContentSize.HALF -> CommunalGridLayoutCard.Size.HALF
-        CommunalContentSize.THIRD -> CommunalGridLayoutCard.Size.THIRD
-    }
+private object Dimensions {
+    val CardWidth = 464.dp
+    val CardHeightFull = 630.dp
+    val CardHeightHalf = 307.dp
+    val CardHeightThird = 199.dp
+    val GridHeight = CardHeightFull
+    val Spacing = 16.dp
 }
diff --git a/packages/SystemUI/res/layout/volume_dnd_icon.xml b/packages/SystemUI/res/layout/volume_dnd_icon.xml
deleted file mode 100644
index 56587b9..0000000
--- a/packages/SystemUI/res/layout/volume_dnd_icon.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<!--
-     Copyright (C) 2018 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.
--->
-<FrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/dnd_icon"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:layout_gravity="bottom"
-    android:layout_marginTop="6dp"
-    android:layout_marginBottom="6dp">
-
-    <ImageView
-        android:layout_width="14dp"
-        android:layout_height="14dp"
-        android:layout_gravity="center"
-        android:src="@*android:drawable/ic_qs_dnd"
-        android:tint="?android:attr/textColorTertiary"/>
-</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/DisableSubpixelTextTransitionListener.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/DisableSubpixelTextTransitionListener.kt
index 4c6d99a..7719246 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/DisableSubpixelTextTransitionListener.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/DisableSubpixelTextTransitionListener.kt
@@ -19,8 +19,8 @@
 import android.view.ViewGroup
 import android.widget.TextView
 import androidx.core.view.forEach
+import com.android.systemui.tracing.traceSection
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
-import com.android.systemui.util.traceSection
 import java.lang.ref.WeakReference
 
 /**
@@ -28,7 +28,7 @@
  * during fold/unfold transitions.
  */
 class DisableSubpixelTextTransitionListener(private val rootView: ViewGroup?) :
-        TransitionProgressListener {
+    TransitionProgressListener {
     private val childrenTextViews: MutableList<WeakReference<TextView>> = mutableListOf()
     private var isTransitionInProgress: Boolean = false
 
@@ -54,7 +54,7 @@
             childrenTextViews.forEach { child ->
                 val childTextView = child.get() ?: return@forEach
                 childTextView.paintFlags =
-                        childTextView.paintFlags and Paint.SUBPIXEL_TEXT_FLAG.inv()
+                    childTextView.paintFlags and Paint.SUBPIXEL_TEXT_FLAG.inv()
             }
             childrenTextViews.clear()
         }
@@ -67,8 +67,8 @@
      * @param childrenTextViews the list to store the retrieved TextView children
      */
     private fun getAllChildTextView(
-            parent: ViewGroup?,
-            childrenTextViews: MutableList<WeakReference<TextView>>
+        parent: ViewGroup?,
+        childrenTextViews: MutableList<WeakReference<TextView>>
     ) {
         parent?.forEach { child ->
             when (child) {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/util/tracing/TraceContextElement.kt b/packages/SystemUI/shared/src/com/android/systemui/tracing/TraceContextElement.kt
similarity index 93%
rename from packages/SystemUI/shared/src/com/android/systemui/util/tracing/TraceContextElement.kt
rename to packages/SystemUI/shared/src/com/android/systemui/tracing/TraceContextElement.kt
index 4d8c545..7d1b65a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/util/tracing/TraceContextElement.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/tracing/TraceContextElement.kt
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.util.tracing
+package com.android.systemui.tracing
 
-import com.android.systemui.util.TraceUtils.Companion.instant
-import com.android.systemui.util.TraceUtils.Companion.traceCoroutine
+import com.android.systemui.tracing.TraceUtils.Companion.instant
+import com.android.systemui.tracing.TraceUtils.Companion.traceCoroutine
 import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.CopyableThreadContextElement
 import kotlinx.coroutines.CoroutineDispatcher
diff --git a/packages/SystemUI/shared/src/com/android/systemui/util/tracing/TraceData.kt b/packages/SystemUI/shared/src/com/android/systemui/tracing/TraceData.kt
similarity index 94%
rename from packages/SystemUI/shared/src/com/android/systemui/util/tracing/TraceData.kt
rename to packages/SystemUI/shared/src/com/android/systemui/tracing/TraceData.kt
index 0ae58fc..b68d38c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/util/tracing/TraceData.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/tracing/TraceData.kt
@@ -14,13 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.systemui.util.tracing
+package com.android.systemui.tracing
 
 import android.os.Build
 import android.util.Log
-import com.android.systemui.util.TraceUtils.Companion.beginSlice
-import com.android.systemui.util.TraceUtils.Companion.endSlice
-import com.android.systemui.util.TraceUtils.Companion.traceCoroutine
+import com.android.systemui.tracing.TraceUtils.Companion.beginSlice
+import com.android.systemui.tracing.TraceUtils.Companion.endSlice
+import com.android.systemui.tracing.TraceUtils.Companion.traceCoroutine
 import kotlin.random.Random
 
 /**
diff --git a/packages/SystemUI/shared/src/com/android/systemui/util/tracing/TraceSection.kt b/packages/SystemUI/shared/src/com/android/systemui/tracing/TraceSection.kt
similarity index 91%
rename from packages/SystemUI/shared/src/com/android/systemui/util/tracing/TraceSection.kt
rename to packages/SystemUI/shared/src/com/android/systemui/tracing/TraceSection.kt
index b70c497..469d9a2 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/util/tracing/TraceSection.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/tracing/TraceSection.kt
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.util.tracing
+package com.android.systemui.tracing
 
-import com.android.systemui.util.TraceUtils.Companion.traceCoroutine
+import com.android.systemui.tracing.TraceUtils.Companion.traceCoroutine
 
 /**
  * Represents a section of code executing in a coroutine. This can be split up into multiple slices
diff --git a/packages/SystemUI/shared/src/com/android/systemui/util/TraceStateLogger.kt b/packages/SystemUI/shared/src/com/android/systemui/tracing/TraceStateLogger.kt
similarity index 97%
rename from packages/SystemUI/shared/src/com/android/systemui/util/TraceStateLogger.kt
rename to packages/SystemUI/shared/src/com/android/systemui/tracing/TraceStateLogger.kt
index 63ea116..3e235f5 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/util/TraceStateLogger.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/tracing/TraceStateLogger.kt
@@ -13,7 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.systemui.util
+
+package com.android.systemui.tracing
 
 import android.os.Trace
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/util/TraceUtils.kt b/packages/SystemUI/shared/src/com/android/systemui/tracing/TraceUtils.kt
similarity index 97%
rename from packages/SystemUI/shared/src/com/android/systemui/util/TraceUtils.kt
rename to packages/SystemUI/shared/src/com/android/systemui/tracing/TraceUtils.kt
index e459034..12a20ae 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/util/TraceUtils.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/tracing/TraceUtils.kt
@@ -14,16 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.systemui.util
+package com.android.systemui.tracing
 
 import android.os.Trace
 import android.os.TraceNameSupplier
 import android.util.Log
-import com.android.systemui.util.tracing.TraceContextElement
-import com.android.systemui.util.tracing.TraceData
-import com.android.systemui.util.tracing.TraceData.Companion.FIRST_VALID_SPAN
-import com.android.systemui.util.tracing.TraceData.Companion.INVALID_SPAN
-import com.android.systemui.util.tracing.threadLocalTrace
+import com.android.systemui.tracing.TraceData.Companion.FIRST_VALID_SPAN
+import com.android.systemui.tracing.TraceData.Companion.INVALID_SPAN
 import java.util.concurrent.atomic.AtomicInteger
 import kotlin.coroutines.CoroutineContext
 import kotlin.coroutines.EmptyCoroutineContext
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 611283f..181aa5c 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -26,7 +26,6 @@
 import static com.android.systemui.flags.Flags.DOZING_MIGRATION_1;
 import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED;
 import static com.android.systemui.flags.Flags.NEW_AOD_TRANSITION;
-import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION;
 import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
 
 import android.annotation.SuppressLint;
@@ -37,7 +36,6 @@
 import android.graphics.Rect;
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricSourceType;
-import android.os.Process;
 import android.os.VibrationAttributes;
 import android.util.DisplayMetrics;
 import android.util.Log;
@@ -793,33 +791,15 @@
 
     @VisibleForTesting
     void vibrateOnTouchExploration() {
-        if (mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
-            mVibrator.performHapticFeedback(
-                    mView,
-                    HapticFeedbackConstants.CONTEXT_CLICK
-            );
-        } else {
-            mVibrator.vibrate(
-                    Process.myUid(),
-                    mContext.getOpPackageName(),
-                    UdfpsController.EFFECT_CLICK,
-                    "lock-icon-down",
-                    TOUCH_VIBRATION_ATTRIBUTES);
-        }
+        mVibrator.performHapticFeedback(
+                mView,
+                HapticFeedbackConstants.CONTEXT_CLICK
+        );
     }
 
     @VisibleForTesting
     void vibrateOnLongPress() {
-        if (mFeatureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
-            mVibrator.performHapticFeedback(mView, UdfpsController.LONG_PRESS);
-        } else {
-            mVibrator.vibrate(
-                    Process.myUid(),
-                    mContext.getOpPackageName(),
-                    UdfpsController.EFFECT_CLICK,
-                    "lock-screen-lock-icon-longpress",
-                    TOUCH_VIBRATION_ATTRIBUTES);
-        }
+        mVibrator.performHapticFeedback(mView, UdfpsController.LONG_PRESS);
     }
 
     private final AuthController.Callback mAuthControllerCallback = new AuthController.Callback() {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/IRadiiAnimationListener.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/IRadiiAnimationListener.java
new file mode 100644
index 0000000..72935f7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/IRadiiAnimationListener.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility.floatingmenu;
+
+interface IRadiiAnimationListener {
+    void onRadiiAnimationUpdate(float[] radii);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java
index ceddee8..761551c 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java
@@ -69,6 +69,7 @@
     private static final int FADE_EFFECT_DURATION_MS = 3000;
 
     private final MenuView mMenuView;
+    private final MenuViewAppearance mMenuViewAppearance;
     private final ValueAnimator mFadeOutAnimator;
     private final Handler mHandler;
     private boolean mIsFadeEffectEnabled;
@@ -81,14 +82,19 @@
     final HashMap<DynamicAnimation.ViewProperty, DynamicAnimation> mPositionAnimations =
             new HashMap<>();
 
-    MenuAnimationController(MenuView menuView) {
+    @VisibleForTesting
+    final RadiiAnimator mRadiiAnimator;
+
+    MenuAnimationController(MenuView menuView, MenuViewAppearance menuViewAppearance) {
         mMenuView = menuView;
+        mMenuViewAppearance = menuViewAppearance;
 
         mHandler = createUiHandler();
         mFadeOutAnimator = new ValueAnimator();
         mFadeOutAnimator.setDuration(FADE_OUT_DURATION_MS);
         mFadeOutAnimator.addUpdateListener(
                 (animation) -> menuView.setAlpha((float) animation.getAnimatedValue()));
+        mRadiiAnimator = new RadiiAnimator(mMenuViewAppearance.getMenuRadii(), mMenuView::setRadii);
     }
 
     void moveToPosition(PointF position) {
@@ -427,6 +433,10 @@
                 .start();
     }
 
+    void startRadiiAnimation(float[] endRadii) {
+        mRadiiAnimator.startAnimation(endRadii);
+    }
+
     private void onSpringAnimationsEnd(PointF position, boolean writeToPosition) {
         mMenuView.onBoundsInParentChanged((int) position.x, (int) position.y);
         constrainPositionAndUpdate(position, writeToPosition);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java
index 92c7259..76808cb 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java
@@ -78,7 +78,7 @@
 
         mMenuViewModel = menuViewModel;
         mMenuViewAppearance = menuViewAppearance;
-        mMenuAnimationController = new MenuAnimationController(this);
+        mMenuAnimationController = new MenuAnimationController(this, menuViewAppearance);
         mAdapter = new AccessibilityTargetAdapter(mTargetFeatures);
         mTargetFeaturesView = new RecyclerView(context);
         mTargetFeaturesView.setAdapter(mAdapter);
@@ -184,9 +184,17 @@
                 insets[3]);
 
         final GradientDrawable gradientDrawable = getContainerViewGradient();
-        gradientDrawable.setCornerRadii(mMenuViewAppearance.getMenuRadii());
         gradientDrawable.setStroke(mMenuViewAppearance.getMenuStrokeWidth(),
                 mMenuViewAppearance.getMenuStrokeColor());
+        if (Flags.floatingMenuRadiiAnimation()) {
+            mMenuAnimationController.startRadiiAnimation(mMenuViewAppearance.getMenuRadii());
+        } else {
+            gradientDrawable.setCornerRadii(mMenuViewAppearance.getMenuRadii());
+        }
+    }
+
+    void setRadii(float[] radii) {
+        getContainerViewGradient().setCornerRadii(radii);
     }
 
     private void onMoveToTucked(boolean isMoveToTucked) {
@@ -391,8 +399,13 @@
         getContainerViewInsetLayer().setLayerInset(INDEX_MENU_ITEM, insets[0], insets[1], insets[2],
                 insets[3]);
 
-        final GradientDrawable gradientDrawable = getContainerViewGradient();
-        gradientDrawable.setCornerRadii(mMenuViewAppearance.getMenuMovingStateRadii());
+        if (Flags.floatingMenuRadiiAnimation()) {
+            mMenuAnimationController.startRadiiAnimation(
+                    mMenuViewAppearance.getMenuMovingStateRadii());
+        } else {
+            final GradientDrawable gradientDrawable = getContainerViewGradient();
+            gradientDrawable.setCornerRadii(mMenuViewAppearance.getMenuMovingStateRadii());
+        }
     }
 
     void onBoundsInParentChanged(int newLeft, int newTop) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/RadiiAnimator.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/RadiiAnimator.java
new file mode 100644
index 0000000..acad36e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/RadiiAnimator.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility.floatingmenu;
+
+import android.animation.Animator;
+import android.animation.ValueAnimator;
+import android.util.MathUtils;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Arrays;
+
+/**
+ * Manages the animation of the floating menu's radii.
+ * <p>
+ * There are 8 output values total. There are 4 corners,
+ * and each corner has a value for the x and y axes.
+ */
+class RadiiAnimator {
+    static final int RADII_COUNT = 8;
+
+    private float[] mStartValues;
+    private float[] mEndValues;
+    private final ValueAnimator mAnimationDriver = ValueAnimator.ofFloat(0.0f, 1.0f);
+
+    RadiiAnimator(float[] initialValues, IRadiiAnimationListener animationListener) {
+        if (initialValues.length != RADII_COUNT) {
+            initialValues = Arrays.copyOf(initialValues, RADII_COUNT);
+        }
+
+        mStartValues = initialValues;
+        mEndValues = initialValues;
+
+        mAnimationDriver.setRepeatCount(0);
+        mAnimationDriver.addUpdateListener(
+                animation -> animationListener.onRadiiAnimationUpdate(
+                        evaluate(animation.getAnimatedFraction())));
+        mAnimationDriver.addListener(new Animator.AnimatorListener() {
+            @Override
+            public void onAnimationStart(@NonNull Animator animation) {
+                animationListener.onRadiiAnimationUpdate(evaluate(/* t = */ 0.0f));
+            }
+
+            @Override
+            public void onAnimationEnd(@NonNull Animator animation) {}
+
+            @Override
+            public void onAnimationCancel(@NonNull Animator animation) {
+                animationListener.onRadiiAnimationUpdate(
+                        evaluate(mAnimationDriver.getAnimatedFraction()));
+            }
+
+            @Override
+            public void onAnimationRepeat(@NonNull Animator animation) {}
+        });
+        mAnimationDriver.setInterpolator(new android.view.animation.BounceInterpolator());
+    }
+
+    void startAnimation(float[] endValues) {
+        if (mAnimationDriver.isStarted()) {
+            mAnimationDriver.cancel();
+            mStartValues = evaluate(mAnimationDriver.getAnimatedFraction());
+        } else {
+            mStartValues = mEndValues;
+        }
+        mEndValues = endValues;
+
+        mAnimationDriver.start();
+    }
+
+    void skipAnimationToEnd() {
+        mAnimationDriver.end();
+    }
+
+    @VisibleForTesting
+    float[] evaluate(float time /* interpolator value between 0.0 and 1.0 */) {
+        float[] out = new float[8];
+        for (int i = 0; i < RADII_COUNT; i++) {
+            out[i] = MathUtils.lerp(mStartValues[i], mEndValues[i], time);
+        }
+        return out;
+    }
+
+    boolean isStarted() {
+        return mAnimationDriver.isStarted();
+    }
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
index 8ea867b..52923a7 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
@@ -27,9 +27,9 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.deviceentry.data.repository.DeviceEntryRepository
+import com.android.systemui.tracing.TraceUtils.Companion.async
+import com.android.systemui.tracing.TraceUtils.Companion.withContext
 import com.android.systemui.user.data.repository.UserRepository
-import com.android.systemui.util.TraceUtils.Companion.async
-import com.android.systemui.util.TraceUtils.Companion.withContext
 import com.android.systemui.util.time.SystemClock
 import javax.inject.Inject
 import kotlin.math.max
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 85122ba..e69eced 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -18,6 +18,7 @@
 
 import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_BIOMETRIC_PROMPT_TRANSITION;
 
 import android.animation.Animator;
@@ -63,7 +64,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.widget.LockPatternUtils;
-import com.android.systemui.res.R;
 import com.android.systemui.biometrics.AuthController.ScaleFactorProvider;
 import com.android.systemui.biometrics.domain.interactor.PromptCredentialInteractor;
 import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor;
@@ -76,8 +76,8 @@
 import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel;
 import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel;
 import com.android.systemui.dagger.qualifiers.Background;
-import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.res.R;
 import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 
@@ -280,7 +280,6 @@
 
     // TODO(b/251476085): remove Config and further decompose these properties out of view classes
     AuthContainerView(@NonNull Config config,
-            @NonNull FeatureFlags featureFlags,
             @NonNull CoroutineScope applicationCoroutineScope,
             @Nullable List<FingerprintSensorPropertiesInternal> fpProps,
             @Nullable List<FaceSensorPropertiesInternal> faceProps,
@@ -295,7 +294,7 @@
             @NonNull Provider<CredentialViewModel> credentialViewModelProvider,
             @NonNull @Background DelayableExecutor bgExecutor,
             @NonNull VibratorHelper vibratorHelper) {
-        this(config, featureFlags, applicationCoroutineScope, fpProps, faceProps,
+        this(config, applicationCoroutineScope, fpProps, faceProps,
                 wakefulnessLifecycle, panelInteractionDetector, userManager, lockPatternUtils,
                 jankMonitor, promptSelectorInteractor, promptCredentialInteractor, promptViewModel,
                 credentialViewModelProvider, new Handler(Looper.getMainLooper()), bgExecutor,
@@ -304,7 +303,6 @@
 
     @VisibleForTesting
     AuthContainerView(@NonNull Config config,
-            @NonNull FeatureFlags featureFlags,
             @NonNull CoroutineScope applicationCoroutineScope,
             @Nullable List<FingerprintSensorPropertiesInternal> fpProps,
             @Nullable List<FaceSensorPropertiesInternal> faceProps,
@@ -368,7 +366,7 @@
         showPrompt(config, layoutInflater, promptViewModel,
                 Utils.findFirstSensorProperties(fpProps, mConfig.mSensorIds),
                 Utils.findFirstSensorProperties(faceProps, mConfig.mSensorIds),
-                vibratorHelper, featureFlags);
+                vibratorHelper);
 
         // TODO: De-dupe the logic with AuthCredentialPasswordView
         setOnKeyListener((v, keyCode, event) -> {
@@ -390,8 +388,7 @@
             @NonNull PromptViewModel viewModel,
             @Nullable FingerprintSensorPropertiesInternal fpProps,
             @Nullable FaceSensorPropertiesInternal faceProps,
-            @NonNull VibratorHelper vibratorHelper,
-            @NonNull FeatureFlags featureFlags
+            @NonNull VibratorHelper vibratorHelper
     ) {
         if (Utils.isBiometricAllowed(config.mPromptInfo)) {
             mPromptSelectorInteractorProvider.get().useBiometricsForAuthentication(
@@ -407,7 +404,7 @@
                     getJankListener(view, TRANSIT,
                             BiometricViewSizeBinder.ANIMATE_MEDIUM_TO_LARGE_DURATION_MS),
                     mBackgroundView, mBiometricCallback, mApplicationCoroutineScope,
-                    vibratorHelper, featureFlags);
+                    vibratorHelper);
 
             // TODO(b/251476085): migrate these dependencies
             if (fpProps != null && fpProps.isAnyUdfpsType()) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index a64e862..05db56f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -78,7 +78,6 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.doze.DozeReceiver;
-import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.keyguard.data.repository.BiometricType;
 import com.android.systemui.statusbar.CommandQueue;
@@ -120,7 +119,6 @@
 
     private final Handler mHandler;
     private final Context mContext;
-    private final FeatureFlags mFeatureFlags;
     private final Execution mExecution;
     private final CommandQueue mCommandQueue;
     private final ActivityTaskManager mActivityTaskManager;
@@ -743,7 +741,6 @@
     }
     @Inject
     public AuthController(Context context,
-            @NonNull FeatureFlags featureFlags,
             @Application CoroutineScope applicationCoroutineScope,
             Execution execution,
             CommandQueue commandQueue,
@@ -770,7 +767,6 @@
             @NonNull UdfpsUtils udfpsUtils,
             @NonNull VibratorHelper vibratorHelper) {
         mContext = context;
-        mFeatureFlags = featureFlags;
         mExecution = execution;
         mUserManager = userManager;
         mLockPatternUtils = lockPatternUtils;
@@ -1316,7 +1312,7 @@
         config.mRequestId = requestId;
         config.mSensorIds = sensorIds;
         config.mScaleProvider = this::getScaleFactor;
-        return new AuthContainerView(config, mFeatureFlags, mApplicationCoroutineScope, mFpProps, mFaceProps,
+        return new AuthContainerView(config, mApplicationCoroutineScope, mFpProps, mFaceProps,
                 wakefulnessLifecycle, panelInteractionDetector, userManager, lockPatternUtils,
                 mInteractionJankMonitor, mPromptCredentialInteractor, mPromptSelectorInteractor,
                 viewModel, mCredentialViewModelProvider, bgExecutor, mVibratorHelper);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
index 40f229b..949c117 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
@@ -63,9 +63,9 @@
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.res.R
+import com.android.systemui.tracing.traceSection
 import com.android.systemui.util.boundsOnScreen
 import com.android.systemui.util.concurrency.DelayableExecutor
-import com.android.systemui.util.traceSection
 import java.io.PrintWriter
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
index ac48b6a..32d9067 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
@@ -49,8 +49,6 @@
 import com.android.systemui.biometrics.ui.viewmodel.PromptMessage
 import com.android.systemui.biometrics.ui.viewmodel.PromptSize
 import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.VibratorHelper
@@ -78,7 +76,6 @@
         legacyCallback: Spaghetti.Callback,
         applicationScope: CoroutineScope,
         vibratorHelper: VibratorHelper,
-        featureFlags: FeatureFlags,
     ): Spaghetti {
         val accessibilityManager = view.context.getSystemService(AccessibilityManager::class.java)!!
 
@@ -380,13 +377,11 @@
                 }
 
                 // Play haptics
-                if (featureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
-                    launch {
-                        viewModel.hapticsToPlay.collect { hapticFeedbackConstant ->
-                            if (hapticFeedbackConstant != HapticFeedbackConstants.NO_HAPTICS) {
-                                vibratorHelper.performHapticFeedback(view, hapticFeedbackConstant)
-                                viewModel.clearHaptics()
-                            }
+                launch {
+                    viewModel.hapticsToPlay.collect { hapticFeedbackConstant ->
+                        if (hapticFeedbackConstant != HapticFeedbackConstants.NO_HAPTICS) {
+                            vibratorHelper.performHapticFeedback(view, hapticFeedbackConstant)
+                            viewModel.clearHaptics()
                         }
                     }
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index e49b4a7..647aaf3 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -29,10 +29,7 @@
 import com.android.systemui.biometrics.shared.model.DisplayRotation
 import com.android.systemui.biometrics.shared.model.PromptKind
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION
 import com.android.systemui.res.R
-import com.android.systemui.statusbar.VibratorHelper
 import javax.inject.Inject
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.coroutineScope
@@ -52,9 +49,7 @@
 constructor(
     displayStateInteractor: DisplayStateInteractor,
     promptSelectorInteractor: PromptSelectorInteractor,
-    private val vibrator: VibratorHelper,
     @Application context: Context,
-    private val featureFlags: FeatureFlags,
 ) {
     /** The set of modalities available for this prompt */
     val modalities: Flow<BiometricModalities> =
@@ -339,7 +334,7 @@
         _message.value = PromptMessage.Error(message)
 
         if (hapticFeedback) {
-            vibrator.error(failedModality)
+            vibrateOnError()
         }
 
         messageJob?.cancel()
@@ -457,7 +452,7 @@
         _message.value = PromptMessage.Empty
 
         if (!needsUserConfirmation) {
-            vibrator.success(modality)
+            vibrateOnSuccess()
         }
 
         messageJob?.cancel()
@@ -495,7 +490,7 @@
         _isAuthenticated.value = authState.asExplicitlyConfirmed()
         _message.value = PromptMessage.Empty
 
-        vibrator.success(authState.authenticatedModality)
+        vibrateOnSuccess()
 
         messageJob?.cancel()
         messageJob = null
@@ -530,20 +525,12 @@
         _forceLargeSize.value = true
     }
 
-    private fun VibratorHelper.success(modality: BiometricModality) {
-        if (featureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
-            _hapticsToPlay.value = HapticFeedbackConstants.CONFIRM
-        } else {
-            vibrateAuthSuccess("$TAG, modality = $modality BP::success")
-        }
+    private fun vibrateOnSuccess() {
+        _hapticsToPlay.value = HapticFeedbackConstants.CONFIRM
     }
 
-    private fun VibratorHelper.error(modality: BiometricModality = BiometricModality.None) {
-        if (featureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
-            _hapticsToPlay.value = HapticFeedbackConstants.REJECT
-        } else {
-            vibrateAuthError("$TAG, modality = $modality BP::error")
-        }
+    private fun vibrateOnError() {
+        _hapticsToPlay.value = HapticFeedbackConstants.REJECT
     }
 
     /** Clears the [hapticsToPlay] variable by setting it to the NO_HAPTICS default. */
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java
index f77f989..a79a654 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java
@@ -75,5 +75,8 @@
 
     /** Indicates an a11y action was made. */
     void onA11yAction();
+
+    /** Initialize the class. */
+    void init();
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java
index 0dfaf0f..d6b9a11 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java
@@ -23,6 +23,10 @@
 /** */
 public class FalsingCollectorFake implements FalsingCollector {
 
+    @Override
+    public void init() {
+    }
+
     @Inject
     public FalsingCollectorFake() {
     }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
index a6b073d..12df96e 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
@@ -32,13 +32,14 @@
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shade.ShadeExpansionStateManager;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
 import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.util.kotlin.JavaAdapter;
 import com.android.systemui.util.sensors.ProximitySensor;
 import com.android.systemui.util.sensors.ThresholdSensor;
 import com.android.systemui.util.sensors.ThresholdSensorEvent;
@@ -65,9 +66,11 @@
     private final ProximitySensor mProximitySensor;
     private final StatusBarStateController mStatusBarStateController;
     private final KeyguardStateController mKeyguardStateController;
+    private final Lazy<ShadeInteractor> mShadeInteractorLazy;
     private final BatteryController mBatteryController;
     private final DockManager mDockManager;
     private final DelayableExecutor mMainExecutor;
+    private final JavaAdapter mJavaAdapter;
     private final SystemClock mSystemClock;
     private final Lazy<SelectedUserInteractor> mUserInteractor;
 
@@ -136,10 +139,11 @@
             ProximitySensor proximitySensor,
             StatusBarStateController statusBarStateController,
             KeyguardStateController keyguardStateController,
-            ShadeExpansionStateManager shadeExpansionStateManager,
+            Lazy<ShadeInteractor> shadeInteractorLazy,
             BatteryController batteryController,
             DockManager dockManager,
             @Main DelayableExecutor mainExecutor,
+            JavaAdapter javaAdapter,
             SystemClock systemClock,
             Lazy<SelectedUserInteractor> userInteractor) {
         mFalsingDataProvider = falsingDataProvider;
@@ -149,12 +153,17 @@
         mProximitySensor = proximitySensor;
         mStatusBarStateController = statusBarStateController;
         mKeyguardStateController = keyguardStateController;
+        mShadeInteractorLazy = shadeInteractorLazy;
         mBatteryController = batteryController;
         mDockManager = dockManager;
         mMainExecutor = mainExecutor;
+        mJavaAdapter = javaAdapter;
         mSystemClock = systemClock;
         mUserInteractor = userInteractor;
+    }
 
+    @Override
+    public void init() {
         mProximitySensor.setTag(PROXIMITY_SENSOR_TAG);
         mProximitySensor.setDelay(SensorManager.SENSOR_DELAY_GAME);
 
@@ -163,7 +172,10 @@
 
         mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateCallback);
 
-        shadeExpansionStateManager.addQsExpansionListener(this::onQsExpansionChanged);
+        mJavaAdapter.alwaysCollectFlow(
+                mShadeInteractorLazy.get().isQsExpanded(),
+                this::onQsExpansionChanged
+        );
 
         mBatteryController.addCallback(mBatteryListener);
         mDockManager.addListener(mDockEventListener);
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorNoOp.kt b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorNoOp.kt
index e5b404f..c5d8c79 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorNoOp.kt
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorNoOp.kt
@@ -23,6 +23,10 @@
 
 @SysUISingleton
 class FalsingCollectorNoOp @Inject constructor() : FalsingCollector {
+    override fun init() {
+        logDebug("NOOP: init")
+    }
+
     override fun onSuccessfulUnlock() {
         logDebug("NOOP: onSuccessfulUnlock")
     }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCoreStartable.kt
new file mode 100644
index 0000000..b79538a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCoreStartable.kt
@@ -0,0 +1,29 @@
+/*
+ * 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.classifier
+
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+/** Initializes classes related to falsing. */
+@SysUISingleton
+class FalsingCoreStartable @Inject constructor(val falsingCollector: FalsingCollector) :
+    CoreStartable {
+    override fun start() {
+        falsingCollector.init()
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java
index 2729b7b..af467ef 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java
@@ -19,9 +19,9 @@
 import android.content.res.Resources;
 import android.view.ViewConfiguration;
 
-import com.android.systemui.res.R;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.res.R;
 import com.android.systemui.scene.shared.flag.SceneContainerFlags;
 import com.android.systemui.statusbar.phone.NotificationTapHelper;
 
@@ -37,7 +37,7 @@
 import javax.inject.Named;
 
 /** Dagger Module for Falsing. */
-@Module
+@Module(includes = {FalsingStartModule.class})
 public interface FalsingModule {
     String BRIGHT_LINE_GESTURE_CLASSIFERS = "bright_line_gesture_classifiers";
     String SINGLE_TAP_TOUCH_SLOP = "falsing_single_tap_touch_slop";
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingStartModule.kt b/packages/SystemUI/src/com/android/systemui/classifier/FalsingStartModule.kt
new file mode 100644
index 0000000..a9f8f37
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingStartModule.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.classifier
+
+import com.android.systemui.CoreStartable
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+
+@Module
+interface FalsingStartModule {
+    /**  */
+    @Binds
+    @IntoMap
+    @ClassKey(FalsingCoreStartable::class)
+    fun bindFalsingCoreStartable(falsingCoreStartable: FalsingCoreStartable?): CoreStartable?
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalContentSize.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalContentSize.kt
index 39a6476..c903709 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalContentSize.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalContentSize.kt
@@ -16,14 +16,19 @@
 
 package com.android.systemui.communal.shared.model
 
-/** Supported sizes for communal content in the layout grid. */
-enum class CommunalContentSize {
+/**
+ * Supported sizes for communal content in the layout grid.
+ *
+ * @param span The span of the content in a column. For example, if FULL is 6, then 3 represents
+ *   HALF, 2 represents THIRD, and 1 represents SIXTH.
+ */
+enum class CommunalContentSize(val span: Int) {
     /** Content takes the full height of the column. */
-    FULL,
+    FULL(6),
 
     /** Content takes half of the height of the column. */
-    HALF,
+    HALF(3),
 
     /** Content takes a third of the height of the column. */
-    THIRD,
+    THIRD(2),
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/model/CommunalContentUiModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/model/CommunalContentUiModel.kt
index 98060dc..b60dc2a 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/model/CommunalContentUiModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/model/CommunalContentUiModel.kt
@@ -9,7 +9,7 @@
  * This model stays in the UI layer.
  */
 data class CommunalContentUiModel(
+    val id: String,
     val view: View,
-    val size: CommunalContentSize,
-    val priority: Int,
+    val size: CommunalContentSize = CommunalContentSize.HALF,
 )
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
index 25c64ea..390b580 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
@@ -20,7 +20,6 @@
 import android.content.Context
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
 import com.android.systemui.communal.domain.interactor.CommunalTutorialInteractor
-import com.android.systemui.communal.shared.model.CommunalContentSize
 import com.android.systemui.communal.ui.model.CommunalContentUiModel
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
@@ -42,16 +41,16 @@
 
     /** List of widgets to be displayed in the communal hub. */
     val widgetContent: Flow<List<CommunalContentUiModel>> =
-        communalInteractor.widgetContent.map {
-            it.map {
+        communalInteractor.widgetContent.map { widgets ->
+            widgets.map Widget@{ widget ->
                 // TODO(b/306406256): As adding and removing widgets functionalities are
                 // supported, cache the host views so they're not recreated each time.
-                val hostView = appWidgetHost.createView(context, it.appWidgetId, it.providerInfo)
-                return@map CommunalContentUiModel(
+                val hostView =
+                    appWidgetHost.createView(context, widget.appWidgetId, widget.providerInfo)
+                return@Widget CommunalContentUiModel(
+                    // TODO(b/308148193): a more scalable solution for unique ids.
+                    id = "widget_${widget.appWidgetId}",
                     view = hostView,
-                    priority = it.priority,
-                    // All widgets have HALF size.
-                    size = CommunalContentSize.HALF,
                 )
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index 1dd4abf..236c5b8 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -48,7 +48,6 @@
 import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.dagger.StartCentralSurfacesModule;
-import com.android.systemui.statusbar.events.StatusBarEventsModule;
 import com.android.systemui.statusbar.phone.DozeServiceHost;
 import com.android.systemui.statusbar.phone.HeadsUpModule;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -99,7 +98,6 @@
         ReferenceScreenshotModule.class,
         RotationLockModule.class,
         SceneContainerFrameworkModule.class,
-        StatusBarEventsModule.class,
         StartCentralSurfacesModule.class,
         VolumeModule.class,
         WallpaperModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index a41bb2f..7915088 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -96,6 +96,7 @@
 import com.android.systemui.statusbar.connectivity.ConnectivityModule;
 import com.android.systemui.statusbar.dagger.StatusBarModule;
 import com.android.systemui.statusbar.disableflags.dagger.DisableFlagsModule;
+import com.android.systemui.statusbar.events.StatusBarEventsModule;
 import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
 import com.android.systemui.statusbar.notification.NotifPipelineFlags;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -209,6 +210,7 @@
         SettingsUtilModule.class,
         SmartRepliesInflationModule.class,
         SmartspaceModule.class,
+        StatusBarEventsModule.class,
         StatusBarModule.class,
         StatusBarPipelineModule.class,
         StatusBarPolicyModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
index 0c8dbe7..2ba687b 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
@@ -31,8 +31,8 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.display.data.DisplayEvent
+import com.android.systemui.tracing.traceSection
 import com.android.systemui.util.Compile
-import com.android.systemui.util.traceSection
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
index 4cade77..323ed98 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
@@ -261,6 +261,13 @@
     }
     //TODO: brightnessfloat change usages to float.
     private int clampToUserSetting(int brightness) {
+        int screenBrightnessModeSetting = mSystemSettings.getIntForUser(
+                Settings.System.SCREEN_BRIGHTNESS_MODE,
+                Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, UserHandle.USER_CURRENT);
+        if (screenBrightnessModeSetting == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC) {
+            return brightness;
+        }
+
         int userSetting = mSystemSettings.getIntForUser(
                 Settings.System.SCREEN_BRIGHTNESS, Integer.MAX_VALUE,
                 UserHandle.USER_CURRENT);
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index f4a9f73..fa98661 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -400,8 +400,7 @@
     // 600- status bar
 
     // TODO(b/291315866): Tracking Bug
-    @JvmField val SIGNAL_CALLBACK_DEPRECATION =
-        unreleasedFlag("signal_callback_deprecation", teamfood = true)
+    @JvmField val SIGNAL_CALLBACK_DEPRECATION = releasedFlag("signal_callback_deprecation")
 
     // TODO(b/301610137): Tracking bug
     @JvmField val NEW_NETWORK_SLICE_UI = unreleasedFlag("new_network_slice_ui", teamfood = true)
@@ -410,19 +409,15 @@
     val FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS =
         releasedFlag("filter_provisioning_network_subscriptions")
 
-    // TODO(b/265892345): Tracking Bug
-    val PLUG_IN_STATUS_BAR_CHIP = releasedFlag("plug_in_status_bar_chip")
-
     // TODO(b/292533677): Tracking Bug
-    val WIFI_TRACKER_LIB_FOR_WIFI_ICON =
-        unreleasedFlag("wifi_tracker_lib_for_wifi_icon", teamfood = true)
+    val WIFI_TRACKER_LIB_FOR_WIFI_ICON = releasedFlag("wifi_tracker_lib_for_wifi_icon")
 
     // TODO(b/293863612): Tracking Bug
     @JvmField val INCOMPATIBLE_CHARGING_BATTERY_ICON =
         releasedFlag("incompatible_charging_battery_icon")
 
     // TODO(b/293585143): Tracking Bug
-    val INSTANT_TETHER = unreleasedFlag("instant_tether", teamfood = true)
+    val INSTANT_TETHER = releasedFlag("instant_tether")
 
     // TODO(b/294588085): Tracking Bug
     val WIFI_SECONDARY_NETWORKS = releasedFlag("wifi_secondary_networks")
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
index 6f491d8..4b98526 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
@@ -36,7 +36,7 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
 import com.android.systemui.keyguard.ui.preview.KeyguardRemotePreviewManager
 import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract
-import com.android.systemui.util.TraceUtils.Companion.runBlocking
+import com.android.systemui.tracing.TraceUtils.Companion.runBlocking
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt
index 6063ce2..5c1b731 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt
@@ -17,7 +17,7 @@
 
 import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
 import com.android.systemui.unfold.updates.screen.ScreenStatusProvider.ScreenListener
-import com.android.systemui.util.traceSection
+import com.android.systemui.tracing.traceSection
 import javax.inject.Inject
 import javax.inject.Singleton
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index 95ac0d8..0e795ae 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -31,7 +31,7 @@
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.shade.data.repository.ShadeRepository
-import com.android.systemui.util.TraceUtils.Companion.launch
+import com.android.systemui.tracing.TraceUtils.Companion.launch
 import com.android.systemui.util.kotlin.Utils.Companion.toQuad
 import com.android.systemui.util.kotlin.Utils.Companion.toTriple
 import com.android.systemui.util.kotlin.sample
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index fe9370f..fbe92e3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -48,7 +48,7 @@
 import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract
 import com.android.systemui.statusbar.phone.SystemUIDialog
 import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.util.TraceUtils.Companion.withContext
+import com.android.systemui.tracing.TraceUtils.Companion.withContext
 import dagger.Lazy
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
index 2a32ddf..601aebe 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
@@ -86,13 +86,13 @@
 import com.android.systemui.statusbar.NotificationMediaManager.isConnectingState
 import com.android.systemui.statusbar.NotificationMediaManager.isPlayingState
 import com.android.systemui.statusbar.notification.row.HybridGroupManager
+import com.android.systemui.tracing.traceSection
 import com.android.systemui.tuner.TunerService
 import com.android.systemui.util.Assert
 import com.android.systemui.util.Utils
 import com.android.systemui.util.concurrency.DelayableExecutor
 import com.android.systemui.util.concurrency.ThreadFactory
 import com.android.systemui.util.time.SystemClock
-import com.android.systemui.util.traceSection
 import java.io.IOException
 import java.io.PrintWriter
 import java.util.concurrent.Executor
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
index 7cb5b3b..d3bc61b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
@@ -38,7 +38,6 @@
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.KeyguardUpdateMonitorCallback
 import com.android.systemui.Dumpable
-import com.android.systemui.res.R
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.dump.DumpManager
@@ -57,6 +56,7 @@
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.qs.PageIndicator
+import com.android.systemui.res.R
 import com.android.systemui.shared.system.SysUiStatsLog
 import com.android.systemui.shared.system.SysUiStatsLog.SMARTSPACE_CARD_REPORTED
 import com.android.systemui.shared.system.SysUiStatsLog.SMART_SPACE_CARD_REPORTED__CARD_TYPE__UNKNOWN_CARD
@@ -66,13 +66,13 @@
 import com.android.systemui.statusbar.notification.collection.provider.OnReorderingAllowedListener
 import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider
 import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.tracing.traceSection
 import com.android.systemui.util.Utils
 import com.android.systemui.util.animation.UniqueObjectHostView
 import com.android.systemui.util.animation.requiresRemeasuring
 import com.android.systemui.util.concurrency.DelayableExecutor
 import com.android.systemui.util.settings.GlobalSettings
 import com.android.systemui.util.time.SystemClock
-import com.android.systemui.util.traceSection
 import java.io.PrintWriter
 import java.util.Locale
 import java.util.TreeMap
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt
index ed6d41e..f3d41aa 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt
@@ -35,7 +35,6 @@
 import androidx.annotation.VisibleForTesting
 import com.android.app.animation.Interpolators
 import com.android.keyguard.KeyguardViewController
-import com.android.systemui.res.R
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.dreams.DreamOverlayStateController
@@ -43,6 +42,7 @@
 import com.android.systemui.media.controls.pipeline.MediaDataManager
 import com.android.systemui.media.dream.MediaDreamComplication
 import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.res.R
 import com.android.systemui.shade.ShadeStateEvents
 import com.android.systemui.shade.ShadeStateEvents.ShadeStateEventsListener
 import com.android.systemui.statusbar.CrossFadeHelper
@@ -53,9 +53,9 @@
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.statusbar.policy.SplitShadeStateController
+import com.android.systemui.tracing.traceSection
 import com.android.systemui.util.animation.UniqueObjectHostView
 import com.android.systemui.util.settings.SecureSettings
-import com.android.systemui.util.traceSection
 import javax.inject.Inject
 
 private val TAG: String = MediaHierarchyManager::class.java.simpleName
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHostStatesManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHostStatesManager.kt
index ae3ce33..0129c49 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHostStatesManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHostStatesManager.kt
@@ -17,8 +17,8 @@
 package com.android.systemui.media.controls.ui
 
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.tracing.traceSection
 import com.android.systemui.util.animation.MeasurementOutput
-import com.android.systemui.util.traceSection
 import javax.inject.Inject
 
 /**
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt
index b436f5d..6b82746 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt
@@ -20,18 +20,18 @@
 import android.content.res.Configuration
 import androidx.annotation.VisibleForTesting
 import androidx.constraintlayout.widget.ConstraintSet
-import com.android.systemui.res.R
 import com.android.systemui.media.controls.models.GutsViewHolder
 import com.android.systemui.media.controls.models.player.MediaViewHolder
 import com.android.systemui.media.controls.models.recommendation.RecommendationViewHolder
 import com.android.systemui.media.controls.ui.MediaCarouselController.Companion.calculateAlpha
 import com.android.systemui.media.controls.util.MediaFlags
+import com.android.systemui.res.R
 import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.tracing.traceSection
 import com.android.systemui.util.animation.MeasurementOutput
 import com.android.systemui.util.animation.TransitionLayout
 import com.android.systemui.util.animation.TransitionLayoutController
 import com.android.systemui.util.animation.TransitionViewState
-import com.android.systemui.util.traceSection
 import java.lang.Float.max
 import java.lang.Float.min
 import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt
index 8103152..13271c3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt
@@ -21,8 +21,8 @@
 import android.os.Looper
 import android.provider.Settings
 import android.view.View
+import android.widget.Switch
 import com.android.internal.logging.MetricsLogger
-import com.android.systemui.res.R
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.plugins.ActivityStarter
@@ -34,6 +34,7 @@
 import com.android.systemui.qs.logging.QSLogger
 import com.android.systemui.qs.tileimpl.QSTileImpl
 import com.android.systemui.qs.tiles.dialog.InternetDialogFactory
+import com.android.systemui.res.R
 import com.android.systemui.statusbar.connectivity.AccessPointController
 import com.android.systemui.statusbar.pipeline.shared.ui.binder.InternetTileBinder
 import com.android.systemui.statusbar.pipeline.shared.ui.model.InternetTileModel
@@ -96,6 +97,7 @@
 
     override fun handleUpdateState(state: QSTile.BooleanState, arg: Any?) {
         state.label = mContext.resources.getString(R.string.quick_settings_internet_label)
+        state.expandedAccessibilityClassName = Switch::class.java.name
 
         model.applyTo(state, mContext)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
index 10d5f59..1416c10 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
@@ -34,7 +34,7 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.settings.DisplayTracker
-import com.android.systemui.util.TraceUtils.Companion.launch
+import com.android.systemui.tracing.TraceUtils.Companion.launch
 import javax.inject.Inject
 import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.CoroutineDispatcher
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
index f1c74c1..8b3548b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
@@ -20,7 +20,7 @@
 import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.util.TraceUtils.Companion.launch
+import com.android.systemui.tracing.TraceUtils.Companion.launch
 import kotlinx.coroutines.CoroutineScope
 import java.util.function.Consumer
 import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt
index d2e4794..c6b2cf5 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt
@@ -25,7 +25,7 @@
 import com.android.systemui.shade.ShadeExpansionStateManager
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
-import com.android.systemui.util.TraceUtils.Companion.launch
+import com.android.systemui.tracing.TraceUtils.Companion.launch
 import kotlinx.coroutines.withContext
 
 /** Provides state from the main SystemUI process on behalf of the Screenshot process. */
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt
index 1eae191..385c813 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt
@@ -20,7 +20,7 @@
 import android.util.Log
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.util.TraceUtils.Companion.async
+import com.android.systemui.tracing.TraceUtils.Companion.async
 import com.google.errorprone.annotations.CanIgnoreReturnValue
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.seconds
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
index 5684605..ccac533 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
@@ -13,7 +13,7 @@
 import com.android.systemui.res.R
 import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_CAPTURE_FAILED
 import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback
-import com.android.systemui.util.TraceUtils.Companion.launch
+import com.android.systemui.tracing.TraceUtils.Companion.launch
 import java.util.function.Consumer
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index cc59f87..dfe6adc 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -1070,7 +1070,6 @@
         mNotificationStackScrollLayoutController.setOnEmptySpaceClickListener(
                 mOnEmptySpaceClickListener);
         mQsController.init();
-        mShadeExpansionStateManager.addQsExpansionListener(this::onQsExpansionChanged);
         mShadeHeadsUpTracker.addTrackingHeadsUpListener(
                 mNotificationStackScrollLayoutController::setTrackingHeadsUp);
         if (!mFeatureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
@@ -4219,7 +4218,7 @@
         return mShadeExpansionStateManager;
     }
 
-    private void onQsExpansionChanged(boolean expanded) {
+    void onQsExpansionChanged(boolean expanded) {
         updateExpandedHeightToMaxHeight();
         setStatusAccessibilityImportance(expanded
                 ? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index 0426388..5114826 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -156,7 +156,6 @@
             KeyguardStateController keyguardStateController,
             ScreenOffAnimationController screenOffAnimationController,
             AuthController authController,
-            ShadeExpansionStateManager shadeExpansionStateManager,
             Lazy<ShadeInteractor> shadeInteractorLazy,
             ShadeWindowLogger logger,
             Lazy<SelectedUserInteractor> userInteractor) {
@@ -185,7 +184,6 @@
                 .addCallback(mStateListener,
                         SysuiStatusBarStateController.RANK_STATUS_BAR_WINDOW_CONTROLLER);
         configurationController.addCallback(this);
-        shadeExpansionStateManager.addQsExpansionListener(this::onQsExpansionChanged);
 
         float desiredPreferredRefreshRate = context.getResources()
                 .getInteger(R.integer.config_keyguardRefreshRate);
@@ -303,6 +301,11 @@
                 mShadeInteractorLazy.get().isAnyExpanded(),
                 this::onShadeOrQsExpanded
         );
+        collectFlow(
+                mWindowRootView,
+                mShadeInteractorLazy.get().isQsExpanded(),
+                this::onQsExpansionChanged
+        );
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
index cc46b23..866cfb4 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
@@ -26,23 +26,27 @@
 import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
 import androidx.constraintlayout.widget.ConstraintSet.START
 import androidx.constraintlayout.widget.ConstraintSet.TOP
-import com.android.systemui.res.R
+import androidx.lifecycle.lifecycleScope
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
 import com.android.systemui.fragments.FragmentService
+import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.navigationbar.NavigationModeController
 import com.android.systemui.plugins.qs.QS
 import com.android.systemui.plugins.qs.QSContainerController
 import com.android.systemui.recents.OverviewProxyService
 import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener
+import com.android.systemui.res.R
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.shared.system.QuickStepContract
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
 import com.android.systemui.statusbar.policy.SplitShadeStateController
 import com.android.systemui.util.LargeScreenUtils
 import com.android.systemui.util.ViewController
 import com.android.systemui.util.concurrency.DelayableExecutor
+import kotlinx.coroutines.launch
 import java.util.function.Consumer
 import javax.inject.Inject
 import kotlin.reflect.KMutableProperty0
@@ -56,7 +60,7 @@
         private val navigationModeController: NavigationModeController,
         private val overviewProxyService: OverviewProxyService,
         private val shadeHeaderController: ShadeHeaderController,
-        private val shadeExpansionStateManager: ShadeExpansionStateManager,
+        private val shadeInteractor: ShadeInteractor,
         private val fragmentService: FragmentService,
         @Main private val delayableExecutor: DelayableExecutor,
         private val featureFlags: FeatureFlags,
@@ -65,7 +69,6 @@
         private val splitShadeStateController: SplitShadeStateController
 ) : ViewController<NotificationsQuickSettingsContainer>(view), QSContainerController {
 
-    private var qsExpanded = false
     private var splitShadeEnabled = false
     private var isQSDetailShowing = false
     private var isQSCustomizing = false
@@ -89,13 +92,6 @@
             taskbarVisible = visible
         }
     }
-    private val shadeQsExpansionListener: ShadeQsExpansionListener =
-        ShadeQsExpansionListener { isQsExpanded ->
-            if (qsExpanded != isQsExpanded) {
-                qsExpanded = isQsExpanded
-                mView.invalidate()
-            }
-        }
 
     // With certain configuration changes (like light/dark changes), the nav bar will disappear
     // for a bit, causing `bottomStableInsets` to be unstable for some time. Debounce the value
@@ -122,6 +118,11 @@
     }
 
     override fun onInit() {
+        mView.repeatWhenAttached {
+            lifecycleScope.launch {
+                shadeInteractor.isQsExpanded.collect{ _ -> mView.invalidate() }
+            }
+        }
         val currentMode: Int = navigationModeController.addListener { mode: Int ->
             isGestureNavigation = QuickStepContract.isGesturalMode(mode)
         }
@@ -137,7 +138,6 @@
     public override fun onViewAttached() {
         updateResources()
         overviewProxyService.addCallback(taskbarVisibilityListener)
-        shadeExpansionStateManager.addQsExpansionListener(shadeQsExpansionListener)
         mView.setInsetsChangedListener(delayedInsetSetter)
         mView.setQSFragmentAttachedListener { qs: QS -> qs.setContainerController(this) }
         mView.setConfigurationChangedListener { updateResources() }
@@ -146,7 +146,6 @@
 
     override fun onViewDetached() {
         overviewProxyService.removeCallback(taskbarVisibilityListener)
-        shadeExpansionStateManager.removeQsExpansionListener(shadeQsExpansionListener)
         mView.removeOnInsetsChangedListener()
         mView.removeQSFragmentAttachedListener()
         mView.setConfigurationChangedListener(null)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
index 0ec7a36..d73fa14 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
@@ -207,12 +207,6 @@
     /** pointerId of the pointer we're currently tracking */
     private int mTrackingPointer;
 
-    /**
-     * Indicates that QS is in expanded state which can happen by:
-     * - single pane shade: expanding shade and then expanding QS
-     * - split shade: just expanding shade (QS are expanded automatically)
-     */
-    private boolean mExpanded;
     /** Indicates QS is at its max height */
     private boolean mFullyExpanded;
     /**
@@ -594,9 +588,8 @@
         return twoFingerDrag || stylusButtonClickDrag || mouseButtonClickDrag;
     }
 
-
     public boolean getExpanded() {
-        return mExpanded;
+        return mShadeRepository.getLegacyIsQsExpanded().getValue();
     }
 
     @VisibleForTesting
@@ -613,7 +606,7 @@
         // close the whole shade with one motion. Also this will be always true when closing
         // split shade as there QS are always expanded so every collapsing motion is motion from
         // expanded QS to closed panel
-        return mExpandImmediate || (mExpanded
+        return mExpandImmediate || (getExpanded()
                 && !isTracking() && !isExpansionAnimating()
                 && !mExpansionFromOverscroll);
     }
@@ -778,11 +771,11 @@
 
     @VisibleForTesting
     void setExpanded(boolean expanded) {
-        boolean changed = mExpanded != expanded;
+        boolean changed = getExpanded() != expanded;
         if (changed) {
-            mExpanded = expanded;
+            mShadeRepository.setLegacyIsQsExpanded(expanded);
             updateQsState();
-            mShadeExpansionStateManager.onQsExpansionChanged(expanded);
+            mPanelViewControllerLazy.get().onQsExpansionChanged(expanded);
             mShadeLog.logQsExpansionChanged("QS Expansion Changed.", expanded,
                     getMinExpansionHeight(), getMaxExpansionHeight(),
                     mStackScrollerOverscrolling, mAnimatorExpand, mAnimating);
@@ -846,7 +839,7 @@
     /** Called when Shade view layout changed. Updates QS expansion or
      * starts size change animation if height has changed. */
     void handleShadeLayoutChanged(int oldMaxHeight) {
-        if (mExpanded && mFullyExpanded) {
+        if (getExpanded() && mFullyExpanded) {
             mExpansionHeight = mMaxExpansionHeight;
             if (mExpansionHeightSetToMaxListener != null) {
                 mExpansionHeightSetToMaxListener.onExpansionHeightSetToMax(true);
@@ -988,24 +981,24 @@
     }
 
     void updateQsState() {
-        boolean qsFullScreen = mExpanded && !mSplitShadeEnabled;
+        boolean qsFullScreen = getExpanded() && !mSplitShadeEnabled;
         mNotificationStackScrollLayoutController.setQsFullScreen(qsFullScreen);
         mNotificationStackScrollLayoutController.setScrollingEnabled(
                 mBarState != KEYGUARD && (!qsFullScreen || mExpansionFromOverscroll));
 
         if (mQsStateUpdateListener != null) {
-            mQsStateUpdateListener.onQsStateUpdated(mExpanded, mStackScrollerOverscrolling);
+            mQsStateUpdateListener.onQsStateUpdated(getExpanded(), mStackScrollerOverscrolling);
         }
 
         if (mQs == null) return;
-        mQs.setExpanded(mExpanded);
+        mQs.setExpanded(getExpanded());
     }
 
     /** update expanded state of QS */
     public void updateExpansion() {
         if (mQs == null) return;
         final float squishiness;
-        if ((mExpandImmediate || mExpanded) && !mSplitShadeEnabled) {
+        if ((mExpandImmediate || getExpanded()) && !mSplitShadeEnabled) {
             squishiness = 1;
         } else if (mTransitioningToFullShadeProgress > 0.0f) {
             squishiness = mLockscreenShadeTransitionController.getQsSquishTransitionFraction();
@@ -1125,7 +1118,7 @@
         mMediaHierarchyManager.setCollapsingShadeFromQS(mExpandedWhenExpandingStarted
                 /* We also start expanding when flinging closed Qs. Let's exclude that */
                 && !mAnimating);
-        if (mExpanded) {
+        if (getExpanded()) {
             onExpansionStarted();
         }
         // Since there are QS tiles in the header now, we need to make sure we start listening
@@ -1234,7 +1227,7 @@
                     Math.min(top / (float) mScrimCornerRadius, 1f));
 
             float bottomRadius = mSplitShadeEnabled ? screenCornerRadius : 0;
-            if (!mExpanded) {
+            if (!getExpanded()) {
                 bottomRadius = calculateBottomCornerRadius(bottomRadius);
             }
             mScrimController.setNotificationBottomRadius(bottomRadius);
@@ -1547,7 +1540,7 @@
     @VisibleForTesting
     void onHeightChanged() {
         mMaxExpansionHeight = isQsFragmentCreated() ? mQs.getDesiredHeight() : 0;
-        if (mExpanded && mFullyExpanded) {
+        if (getExpanded() && mFullyExpanded) {
             mExpansionHeight = mMaxExpansionHeight;
             if (mExpansionHeightSetToMaxListener != null) {
                 mExpansionHeightSetToMaxListener.onExpansionHeightSetToMax(true);
@@ -2083,7 +2076,7 @@
         ipw.print("mTrackingPointer=");
         ipw.println(mTrackingPointer);
         ipw.print("mExpanded=");
-        ipw.println(mExpanded);
+        ipw.println(getExpanded());
         ipw.print("mFullyExpanded=");
         ipw.println(mFullyExpanded);
         ipw.print("mExpandImmediate=");
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt
index 9493989..fca98f5 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt
@@ -36,15 +36,12 @@
 class ShadeExpansionStateManager @Inject constructor() : ShadeStateEvents {
 
     private val expansionListeners = CopyOnWriteArrayList<ShadeExpansionListener>()
-    private val qsExpansionListeners = CopyOnWriteArrayList<ShadeQsExpansionListener>()
     private val stateListeners = CopyOnWriteArrayList<ShadeStateListener>()
     private val shadeStateEventsListeners = CopyOnWriteArrayList<ShadeStateEventsListener>()
 
     @PanelState private var state: Int = STATE_CLOSED
     @FloatRange(from = 0.0, to = 1.0) private var fraction: Float = 0f
     private var expanded: Boolean = false
-    private var qsExpanded: Boolean = false
-    private var qsExpansionFraction = 0f
     private var tracking: Boolean = false
     private var dragDownPxAmount: Float = 0f
 
@@ -64,15 +61,6 @@
         expansionListeners.remove(listener)
     }
 
-    fun addQsExpansionListener(listener: ShadeQsExpansionListener) {
-        qsExpansionListeners.add(listener)
-        listener.onQsExpansionChanged(qsExpanded)
-    }
-
-    fun removeQsExpansionListener(listener: ShadeQsExpansionListener) {
-        qsExpansionListeners.remove(listener)
-    }
-
     /** Adds a listener that will be notified when the panel state has changed. */
     fun addStateListener(listener: ShadeStateListener) {
         stateListeners.add(listener)
@@ -153,14 +141,6 @@
         expansionListeners.forEach { it.onPanelExpansionChanged(expansionChangeEvent) }
     }
 
-    /** Called when the quick settings expansion changes to fully expanded or collapsed. */
-    fun onQsExpansionChanged(qsExpanded: Boolean) {
-        this.qsExpanded = qsExpanded
-
-        debugLog("qsExpanded=$qsExpanded")
-        qsExpansionListeners.forEach { it.onQsExpansionChanged(qsExpanded) }
-    }
-
     /** Updates the panel state if necessary. */
     fun updateState(@PanelState state: Int) {
         debugLog(
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
index 024c8e3..e2e4556 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
@@ -83,6 +83,17 @@
     val legacyExpandedOrAwaitingInputTransfer: StateFlow<Boolean>
 
     /**
+     * QuickSettingsController.mExpanded as a flow. Indicates that QS is in expanded state:
+     * - single pane shade: expanding shade and then expanding QS
+     * - split shade: just expanding shade (QS are expanded automatically)
+     */
+    @Deprecated("Use ShadeInteractor instead") val legacyIsQsExpanded: StateFlow<Boolean>
+
+    /** Sets whether QS is expanded. */
+    @Deprecated("Use ShadeInteractor instead")
+    fun setLegacyIsQsExpanded(legacyIsQsExpanded: Boolean)
+
+    /**
      * Sets whether the expansion fraction is greater than zero or NPVC is about to accept an input
      * transfer from the status bar, home screen, or trackpad.
      */
@@ -175,6 +186,15 @@
     override val legacyExpandedOrAwaitingInputTransfer: StateFlow<Boolean> =
         _legacyExpandedOrAwaitingInputTransfer.asStateFlow()
 
+    private val _legacyIsQsExpanded = MutableStateFlow(false)
+    @Deprecated("Use ShadeInteractor instead")
+    override val legacyIsQsExpanded: StateFlow<Boolean> = _legacyIsQsExpanded.asStateFlow()
+
+    @Deprecated("Use ShadeInteractor instead")
+    override fun setLegacyIsQsExpanded(legacyIsQsExpanded: Boolean) {
+        _legacyIsQsExpanded.value = legacyIsQsExpanded
+    }
+
     @Deprecated("Use ShadeInteractor instead")
     override fun setLegacyExpandedOrAwaitingInputTransfer(
         legacyExpandedOrAwaitingInputTransfer: Boolean
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
index f043c71..a4c4503 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
@@ -121,16 +121,37 @@
 
     /**
      * The amount [0-1] QS has been opened. Normal shade with notifications (QQS) visible will
-     * report 0f.
+     * report 0f. If split shade is enabled, value matches shadeExpansion.
      */
     val qsExpansion: StateFlow<Float> =
         if (sceneContainerFlags.isEnabled()) {
-            sceneBasedExpansion(sceneInteractorProvider.get(), SceneKey.QuickSettings)
+            val qsExp = sceneBasedExpansion(sceneInteractorProvider.get(), SceneKey.QuickSettings)
+            combine(isSplitShadeEnabled, shadeExpansion, qsExp) {
+                    isSplitShadeEnabled,
+                    shadeExp,
+                    qsExp ->
+                    if (isSplitShadeEnabled) {
+                        shadeExp
+                    } else {
+                        qsExp
+                    }
+                }
                 .stateIn(scope, SharingStarted.Eagerly, 0f)
         } else {
             repository.qsExpansion
         }
 
+    /** Whether Quick Settings is expanded a non-zero amount. */
+    val isQsExpanded: StateFlow<Boolean> =
+        if (sceneContainerFlags.isEnabled()) {
+            qsExpansion
+                .map { it > 0 }
+                .distinctUntilChanged()
+                .stateIn(scope, SharingStarted.Eagerly, false)
+        } else {
+            repository.legacyIsQsExpanded
+        }
+
     /** The amount [0-1] either QS or the shade has been opened. */
     val anyExpansion: StateFlow<Float> =
         combine(shadeExpansion, qsExpansion) { shadeExp, qsExp -> maxOf(shadeExp, qsExp) }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
index 4ea7026..e19fcd0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
@@ -32,17 +32,15 @@
 import com.android.systemui.Dumpable
 import com.android.systemui.Gefingerpoken
 import com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN
-import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.res.R
-import com.android.systemui.shade.ShadeExpansionStateManager
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.row.ExpandableView
-import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
 import com.android.systemui.statusbar.phone.KeyguardBypassController
 import com.android.systemui.statusbar.policy.ConfigurationController
@@ -64,13 +62,11 @@
     private val wakeUpCoordinator: NotificationWakeUpCoordinator,
     private val bypassController: KeyguardBypassController,
     private val headsUpManager: HeadsUpManager,
-    private val roundnessManager: NotificationRoundnessManager,
     configurationController: ConfigurationController,
     private val statusBarStateController: StatusBarStateController,
     private val falsingManager: FalsingManager,
-    shadeExpansionStateManager: ShadeExpansionStateManager,
+    private val shadeInteractor: ShadeInteractor,
     private val lockscreenShadeTransitionController: LockscreenShadeTransitionController,
-    private val falsingCollector: FalsingCollector,
     dumpManager: DumpManager
 ) : Gefingerpoken, Dumpable {
     companion object {
@@ -115,7 +111,6 @@
 
     private val isFalseTouch: Boolean
         get() = falsingManager.isFalseTouch(NOTIFICATION_DRAG_DOWN)
-    var qsExpanded: Boolean = false
     var pulseExpandAbortListener: Runnable? = null
     var bouncerShowing: Boolean = false
 
@@ -127,12 +122,6 @@
             }
         })
 
-        shadeExpansionStateManager.addQsExpansionListener { isQsExpanded ->
-            if (qsExpanded != isQsExpanded) {
-                qsExpanded = isQsExpanded
-            }
-        }
-
         mPowerManager = context.getSystemService(PowerManager::class.java)
         dumpManager.registerDumpable(this)
     }
@@ -148,7 +137,8 @@
     }
 
     private fun canHandleMotionEvent(): Boolean {
-        return wakeUpCoordinator.canShowPulsingHuns && !qsExpanded && !bouncerShowing
+        return wakeUpCoordinator.canShowPulsingHuns && !shadeInteractor.isQsExpanded.value &&
+            !bouncerShowing
     }
 
     private fun startExpansion(event: MotionEvent): Boolean {
@@ -379,7 +369,6 @@
             it.println("isExpanding: $isExpanding")
             it.println("leavingLockscreen: $leavingLockscreen")
             it.println("mPulsing: $mPulsing")
-            it.println("qsExpanded: $qsExpanded")
             it.println("bouncerShowing: $bouncerShowing")
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
index 5af7cfc..a36d36c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar.events
 
-import androidx.core.animation.Animator
 import android.annotation.UiThread
 import android.graphics.Point
 import android.graphics.Rect
@@ -24,13 +23,15 @@
 import android.view.Gravity
 import android.view.View
 import android.widget.FrameLayout
-import com.android.internal.annotations.GuardedBy
-import com.android.systemui.res.R
+import androidx.core.animation.Animator
 import com.android.app.animation.Interpolators
+import com.android.internal.annotations.GuardedBy
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.shade.ShadeExpansionStateManager
+import com.android.systemui.res.R
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.StatusBarState.SHADE
 import com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED
 import com.android.systemui.statusbar.phone.StatusBarContentInsetsChangedListener
@@ -43,6 +44,8 @@
 import com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE
 import com.android.systemui.util.leak.RotationUtils.ROTATION_UPSIDE_DOWN
 import com.android.systemui.util.leak.RotationUtils.Rotation
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
 import java.util.concurrent.Executor
 import javax.inject.Inject
 
@@ -64,11 +67,12 @@
 @SysUISingleton
 open class PrivacyDotViewController @Inject constructor(
     @Main private val mainExecutor: Executor,
+    @Application scope: CoroutineScope,
     private val stateController: StatusBarStateController,
     private val configurationController: ConfigurationController,
     private val contentInsetsProvider: StatusBarContentInsetsProvider,
     private val animationScheduler: SystemStatusAnimationScheduler,
-    shadeExpansionStateManager: ShadeExpansionStateManager
+    shadeInteractor: ShadeInteractor?
 ) {
     private lateinit var tl: View
     private lateinit var tr: View
@@ -135,10 +139,12 @@
             }
         })
 
-        shadeExpansionStateManager.addQsExpansionListener { isQsExpanded ->
-            dlog("setQsExpanded $isQsExpanded")
-            synchronized(lock) {
-                nextViewState = nextViewState.copy(qsExpanded = isQsExpanded)
+        scope.launch {
+            shadeInteractor?.isQsExpanded?.collect { isQsExpanded ->
+                dlog("setQsExpanded $isQsExpanded")
+                synchronized(lock) {
+                    nextViewState = nextViewState.copy(qsExpanded = isQsExpanded)
+                }
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusBarEventsModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusBarEventsModule.kt
index 84796f9..ed96482 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusBarEventsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusBarEventsModule.kt
@@ -17,19 +17,11 @@
 package com.android.systemui.statusbar.events
 
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.LogBufferFactory
-import com.android.systemui.statusbar.window.StatusBarWindowController
-import com.android.systemui.util.concurrency.DelayableExecutor
-import com.android.systemui.util.time.SystemClock
+import dagger.Binds
 import dagger.Module
 import dagger.Provides
-import kotlinx.coroutines.CoroutineScope
 
 @Module
 interface StatusBarEventsModule {
@@ -42,41 +34,11 @@
         fun provideSystemStatusAnimationSchedulerLogBuffer(factory: LogBufferFactory): LogBuffer {
             return factory.create("SystemStatusAnimationSchedulerLog", 60)
         }
-
-        @Provides
-        @SysUISingleton
-        fun provideSystemStatusAnimationScheduler(
-                featureFlags: FeatureFlags,
-                coordinator: SystemEventCoordinator,
-                chipAnimationController: SystemEventChipAnimationController,
-                statusBarWindowController: StatusBarWindowController,
-                dumpManager: DumpManager,
-                systemClock: SystemClock,
-                @Application coroutineScope: CoroutineScope,
-                @Main executor: DelayableExecutor,
-                logger: SystemStatusAnimationSchedulerLogger
-        ): SystemStatusAnimationScheduler {
-            return if (featureFlags.isEnabled(Flags.PLUG_IN_STATUS_BAR_CHIP)) {
-                SystemStatusAnimationSchedulerImpl(
-                        coordinator,
-                        chipAnimationController,
-                        statusBarWindowController,
-                        dumpManager,
-                        systemClock,
-                        coroutineScope,
-                        logger
-                )
-            } else {
-                SystemStatusAnimationSchedulerLegacyImpl(
-                        coordinator,
-                        chipAnimationController,
-                        statusBarWindowController,
-                        dumpManager,
-                        systemClock,
-                        executor
-                )
-            }
-        }
     }
-}
 
+    @Binds
+    @SysUISingleton
+    fun bindSystemStatusAnimationScheduler(
+        systemStatusAnimationSchedulerImpl: SystemStatusAnimationSchedulerImpl
+    ): SystemStatusAnimationScheduler
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
index 73c0bfe..fec1765 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
@@ -32,8 +32,6 @@
 import androidx.core.animation.ValueAnimator
 import com.android.internal.annotations.VisibleForTesting
 import com.android.systemui.res.R
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.statusbar.phone.StatusBarContentInsetsChangedListener
 import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
 import com.android.systemui.statusbar.window.StatusBarWindowController
@@ -47,8 +45,7 @@
 class SystemEventChipAnimationController @Inject constructor(
     private val context: Context,
     private val statusBarWindowController: StatusBarWindowController,
-    private val contentInsetsProvider: StatusBarContentInsetsProvider,
-    private val featureFlags: FeatureFlags,
+    private val contentInsetsProvider: StatusBarContentInsetsProvider
 ) : SystemStatusAnimationCallback {
 
     private lateinit var animationWindowView: FrameLayout
@@ -317,15 +314,8 @@
                 it.marginEnd = marginEnd
             }
 
-    private fun initializeAnimRect() = if (featureFlags.isEnabled(Flags.PLUG_IN_STATUS_BAR_CHIP)) {
-        animRect.set(chipBounds)
-    } else {
-        animRect.set(
-                chipLeft,
-                currentAnimatedView!!.view.top,
-                chipRight,
-                currentAnimatedView!!.view.bottom)
-    }
+    private fun initializeAnimRect() = animRect.set(chipBounds)
+
 
     /**
      * To be called during an animation, sets the width and updates the current animated chip view
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt
index e9d5dec..a73d517 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt
@@ -24,8 +24,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
+import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor.State
 import com.android.systemui.privacy.PrivacyChipBuilder
 import com.android.systemui.privacy.PrivacyItem
 import com.android.systemui.privacy.PrivacyItemController
@@ -49,7 +48,6 @@
     private val batteryController: BatteryController,
     private val privacyController: PrivacyItemController,
     private val context: Context,
-    private val featureFlags: FeatureFlags,
     @Application private val appScope: CoroutineScope,
     connectedDisplayInteractor: ConnectedDisplayInteractor
 ) {
@@ -76,9 +74,7 @@
     }
 
     fun notifyPluggedIn(@IntRange(from = 0, to = 100) batteryLevel: Int) {
-        if (featureFlags.isEnabled(Flags.PLUG_IN_STATUS_BAR_CHIP)) {
-            scheduler.onStatusEvent(BatteryEvent(batteryLevel))
-        }
+        scheduler.onStatusEvent(BatteryEvent(batteryLevel))
     }
 
     fun notifyPrivacyItemsEmpty() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerLegacyImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerLegacyImpl.kt
deleted file mode 100644
index 6b5a548..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerLegacyImpl.kt
+++ /dev/null
@@ -1,315 +0,0 @@
-/*
- * Copyright (C) 2021 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.events
-
-import android.os.Process
-import android.provider.DeviceConfig
-import android.util.Log
-import androidx.core.animation.Animator
-import androidx.core.animation.AnimatorListenerAdapter
-import androidx.core.animation.AnimatorSet
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.statusbar.window.StatusBarWindowController
-import com.android.systemui.util.Assert
-import com.android.systemui.util.concurrency.DelayableExecutor
-import com.android.systemui.util.time.SystemClock
-import java.io.PrintWriter
-import javax.inject.Inject
-
-/**
- * Dead-simple scheduler for system status events. Obeys the following principles (all values TBD):
- * ```
- *      - Avoiding log spam by only allowing 12 events per minute (1event/5s)
- *      - Waits 100ms to schedule any event for debouncing/prioritization
- *      - Simple prioritization: Privacy > Battery > connectivity (encoded in [StatusEvent])
- *      - Only schedules a single event, and throws away lowest priority events
- * ```
- *
- * There are 4 basic stages of animation at play here:
- * ```
- *      1. System chrome animation OUT
- *      2. Chip animation IN
- *      3. Chip animation OUT; potentially into a dot
- *      4. System chrome animation IN
- * ```
- *
- * Thus we can keep all animations synchronized with two separate ValueAnimators, one for system
- * chrome and the other for the chip. These can animate from 0,1 and listeners can parameterize
- * their respective views based on the progress of the animator. Interpolation differences TBD
- */
-open class SystemStatusAnimationSchedulerLegacyImpl
-@Inject
-constructor(
-    private val coordinator: SystemEventCoordinator,
-    private val chipAnimationController: SystemEventChipAnimationController,
-    private val statusBarWindowController: StatusBarWindowController,
-    private val dumpManager: DumpManager,
-    private val systemClock: SystemClock,
-    @Main private val executor: DelayableExecutor
-) : SystemStatusAnimationScheduler {
-
-    companion object {
-        private const val PROPERTY_ENABLE_IMMERSIVE_INDICATOR = "enable_immersive_indicator"
-    }
-
-    fun isImmersiveIndicatorEnabled(): Boolean {
-        return DeviceConfig.getBoolean(
-            DeviceConfig.NAMESPACE_PRIVACY,
-            PROPERTY_ENABLE_IMMERSIVE_INDICATOR,
-            true
-        )
-    }
-
-    @SystemAnimationState private var animationState: Int = IDLE
-
-    /** True if the persistent privacy dot should be active */
-    var hasPersistentDot = false
-        protected set
-
-    private var scheduledEvent: StatusEvent? = null
-
-    val listeners = mutableSetOf<SystemStatusAnimationCallback>()
-
-    init {
-        coordinator.attachScheduler(this)
-        dumpManager.registerDumpable(TAG, this)
-    }
-
-    @SystemAnimationState override fun getAnimationState() = animationState
-
-    override fun onStatusEvent(event: StatusEvent) {
-        // Ignore any updates until the system is up and running. However, for important events that
-        // request to be force visible (like privacy), ignore whether it's too early.
-        if ((isTooEarly() && !event.forceVisible) || !isImmersiveIndicatorEnabled()) {
-            return
-        }
-
-        // Don't deal with threading for now (no need let's be honest)
-        Assert.isMainThread()
-        if (
-            (event.priority > (scheduledEvent?.priority ?: -1)) &&
-                animationState != ANIMATING_OUT &&
-                animationState != SHOWING_PERSISTENT_DOT
-        ) {
-            // events can only be scheduled if a higher priority or no other event is in progress
-            if (DEBUG) {
-                Log.d(TAG, "scheduling event $event")
-            }
-
-            scheduleEvent(event)
-        } else if (scheduledEvent?.shouldUpdateFromEvent(event) == true) {
-            if (DEBUG) {
-                Log.d(TAG, "updating current event from: $event. animationState=$animationState")
-            }
-            scheduledEvent?.updateFromEvent(event)
-            if (event.forceVisible) {
-                hasPersistentDot = true
-                // If we missed the chance to show the persistent dot, do it now
-                if (animationState == IDLE) {
-                    notifyTransitionToPersistentDot()
-                }
-            }
-        } else {
-            if (DEBUG) {
-                Log.d(TAG, "ignoring event $event")
-            }
-        }
-    }
-
-    override fun removePersistentDot() {
-        if (!hasPersistentDot || !isImmersiveIndicatorEnabled()) {
-            return
-        }
-
-        hasPersistentDot = false
-        notifyHidePersistentDot()
-        return
-    }
-
-    fun isTooEarly(): Boolean {
-        return systemClock.uptimeMillis() - Process.getStartUptimeMillis() < MIN_UPTIME
-    }
-
-    /** Clear the scheduled event (if any) and schedule a new one */
-    private fun scheduleEvent(event: StatusEvent) {
-        scheduledEvent = event
-
-        if (event.forceVisible) {
-            hasPersistentDot = true
-        }
-
-        // If animations are turned off, we'll transition directly to the dot
-        if (!event.showAnimation && event.forceVisible) {
-            notifyTransitionToPersistentDot()
-            scheduledEvent = null
-            return
-        }
-
-        chipAnimationController.prepareChipAnimation(scheduledEvent!!.viewCreator)
-        animationState = ANIMATION_QUEUED
-        executor.executeDelayed({ runChipAnimation() }, DEBOUNCE_DELAY)
-    }
-
-    /**
-     * 1. Define a total budget for the chip animation (1500ms)
-     * 2. Send out callbacks to listeners so that they can generate animations locally
-     * 3. Update the scheduler state so that clients know where we are
-     * 4. Maybe: provide scaffolding such as: dot location, margins, etc
-     * 5. Maybe: define a maximum animation length and enforce it. Probably only doable if we
-     *    collect all of the animators and run them together.
-     */
-    private fun runChipAnimation() {
-        statusBarWindowController.setForceStatusBarVisible(true)
-        animationState = ANIMATING_IN
-
-        val animSet = collectStartAnimations()
-        if (animSet.totalDuration > 500) {
-            throw IllegalStateException(
-                "System animation total length exceeds budget. " +
-                    "Expected: 500, actual: ${animSet.totalDuration}"
-            )
-        }
-        animSet.addListener(
-            object : AnimatorListenerAdapter() {
-                override fun onAnimationEnd(animation: Animator) {
-                    animationState = RUNNING_CHIP_ANIM
-                }
-            }
-        )
-        animSet.start()
-
-        executor.executeDelayed(
-            {
-                val animSet2 = collectFinishAnimations()
-                animationState = ANIMATING_OUT
-                animSet2.addListener(
-                    object : AnimatorListenerAdapter() {
-                        override fun onAnimationEnd(animation: Animator) {
-                            animationState =
-                                if (hasPersistentDot) {
-                                    SHOWING_PERSISTENT_DOT
-                                } else {
-                                    IDLE
-                                }
-
-                            statusBarWindowController.setForceStatusBarVisible(false)
-                        }
-                    }
-                )
-                animSet2.start()
-                scheduledEvent = null
-            },
-            DISPLAY_LENGTH
-        )
-    }
-
-    private fun collectStartAnimations(): AnimatorSet {
-        val animators = mutableListOf<Animator>()
-        listeners.forEach { listener ->
-            listener.onSystemEventAnimationBegin()?.let { anim -> animators.add(anim) }
-        }
-        animators.add(chipAnimationController.onSystemEventAnimationBegin())
-        val animSet = AnimatorSet().also { it.playTogether(animators) }
-
-        return animSet
-    }
-
-    private fun collectFinishAnimations(): AnimatorSet {
-        val animators = mutableListOf<Animator>()
-        listeners.forEach { listener ->
-            listener.onSystemEventAnimationFinish(hasPersistentDot)?.let { anim ->
-                animators.add(anim)
-            }
-        }
-        animators.add(chipAnimationController.onSystemEventAnimationFinish(hasPersistentDot))
-        if (hasPersistentDot) {
-            val dotAnim = notifyTransitionToPersistentDot()
-            if (dotAnim != null) {
-                animators.add(dotAnim)
-            }
-        }
-        val animSet = AnimatorSet().also { it.playTogether(animators) }
-
-        return animSet
-    }
-
-    private fun notifyTransitionToPersistentDot(): Animator? {
-        val anims: List<Animator> =
-            listeners.mapNotNull {
-                it.onSystemStatusAnimationTransitionToPersistentDot(
-                    scheduledEvent?.contentDescription
-                )
-            }
-        if (anims.isNotEmpty()) {
-            val aSet = AnimatorSet()
-            aSet.playTogether(anims)
-            return aSet
-        }
-
-        return null
-    }
-
-    private fun notifyHidePersistentDot(): Animator? {
-        val anims: List<Animator> = listeners.mapNotNull { it.onHidePersistentDot() }
-
-        if (animationState == SHOWING_PERSISTENT_DOT) {
-            animationState = IDLE
-        }
-
-        if (anims.isNotEmpty()) {
-            val aSet = AnimatorSet()
-            aSet.playTogether(anims)
-            return aSet
-        }
-
-        return null
-    }
-
-    override fun addCallback(listener: SystemStatusAnimationCallback) {
-        Assert.isMainThread()
-
-        if (listeners.isEmpty()) {
-            coordinator.startObserving()
-        }
-        listeners.add(listener)
-    }
-
-    override fun removeCallback(listener: SystemStatusAnimationCallback) {
-        Assert.isMainThread()
-
-        listeners.remove(listener)
-        if (listeners.isEmpty()) {
-            coordinator.stopObserving()
-        }
-    }
-
-    override fun dump(pw: PrintWriter, args: Array<out String>) {
-        pw.println("Scheduled event: $scheduledEvent")
-        pw.println("Has persistent privacy dot: $hasPersistentDot")
-        pw.println("Animation state: $animationState")
-        pw.println("Listeners:")
-        if (listeners.isEmpty()) {
-            pw.println("(none)")
-        } else {
-            listeners.forEach { pw.println("  $it") }
-        }
-    }
-}
-
-private const val DEBUG = false
-private const val TAG = "SystemStatusAnimationSchedulerLegacyImpl"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreImpl.kt
index 5acc50a..b2bdb72 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreImpl.kt
@@ -21,7 +21,7 @@
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.util.Assert
 import com.android.systemui.util.ListenerSet
-import com.android.systemui.util.traceSection
+import com.android.systemui.tracing.traceSection
 import java.util.Collections.unmodifiableList
 import java.util.concurrent.Executor
 import java.util.concurrent.atomic.AtomicReference
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
index e0c4bfa..860697b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
@@ -26,7 +26,7 @@
 import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
 import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT
 import com.android.systemui.statusbar.phone.NotificationIconAreaController
-import com.android.systemui.util.traceSection
+import com.android.systemui.tracing.traceSection
 import javax.inject.Inject
 
 /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
index c0f674846..e8afac5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
@@ -28,7 +28,7 @@
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.util.Compile
-import com.android.systemui.util.traceSection
+import com.android.systemui.tracing.traceSection
 import javax.inject.Inject
 
 /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifEvent.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifEvent.kt
index e06e2d0..1f6f42d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifEvent.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifEvent.kt
@@ -23,7 +23,7 @@
 import com.android.systemui.statusbar.notification.collection.NotifCollection
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.util.NamedListenerSet
-import com.android.systemui.util.traceSection
+import com.android.systemui.tracing.traceSection
 
 /**
  * Set of classes that represent the various events that [NotifCollection] can dispatch to
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
index d234e54..ca8e4fe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
@@ -23,7 +23,7 @@
 import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection
 import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider
 import com.android.systemui.util.Compile
-import com.android.systemui.util.traceSection
+import com.android.systemui.tracing.traceSection
 
 /**
  * Converts a notif list (the output of the ShadeListBuilder) into a NodeSpec, an abstract
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManager.kt
index 3061522..c2791a0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManager.kt
@@ -26,7 +26,7 @@
 import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderEntryListener
 import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderGroupListener
 import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderListListener
-import com.android.systemui.util.traceSection
+import com.android.systemui.tracing.traceSection
 import javax.inject.Inject
 
 /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
index 1a88815..c6d8500 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
@@ -18,7 +18,7 @@
 
 import android.annotation.MainThread
 import android.view.View
-import com.android.systemui.util.traceSection
+import com.android.systemui.tracing.traceSection
 
 /**
  * Given a "spec" that describes a "tree" of views, adds and removes views from the
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
index df8e87f..2c59ee8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
@@ -26,7 +26,7 @@
 import com.android.systemui.statusbar.notification.collection.PipelineDumper
 import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer
-import com.android.systemui.util.traceSection
+import com.android.systemui.tracing.traceSection
 import dagger.assisted.Assisted
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
index 84678aa..05c88e0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
@@ -34,7 +34,7 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
-import com.android.systemui.util.traceSection
+import com.android.systemui.tracing.traceSection
 import javax.inject.Inject
 
 /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
index 301ddbf..f2ade34 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
@@ -16,6 +16,9 @@
 
 package com.android.systemui.statusbar.notification.interruption;
 
+import static android.provider.Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED;
+import static android.provider.Settings.Global.HEADS_UP_OFF;
+
 import static com.android.systemui.statusbar.StatusBarState.SHADE;
 import static com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.FSI_SUPPRESSED_NO_HUN_OR_KEYGUARD;
 import static com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.FSI_SUPPRESSED_SUPPRESSIVE_BUBBLE_METADATA;
@@ -24,12 +27,10 @@
 
 import android.app.Notification;
 import android.app.NotificationManager;
-import android.content.ContentResolver;
 import android.database.ContentObserver;
 import android.hardware.display.AmbientDisplayConfiguration;
 import android.os.Handler;
 import android.os.PowerManager;
-import android.provider.Settings;
 import android.service.notification.StatusBarNotification;
 
 import androidx.annotation.NonNull;
@@ -48,6 +49,8 @@
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.settings.GlobalSettings;
+import com.android.systemui.util.time.SystemClock;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -66,7 +69,6 @@
     private final List<NotificationInterruptSuppressor> mSuppressors = new ArrayList<>();
     private final StatusBarStateController mStatusBarStateController;
     private final KeyguardStateController mKeyguardStateController;
-    private final ContentResolver mContentResolver;
     private final PowerManager mPowerManager;
     private final AmbientDisplayConfiguration mAmbientDisplayConfiguration;
     private final BatteryController mBatteryController;
@@ -77,6 +79,8 @@
     private final UiEventLogger mUiEventLogger;
     private final UserTracker mUserTracker;
     private final DeviceProvisionedController mDeviceProvisionedController;
+    private final SystemClock mSystemClock;
+    private final GlobalSettings mGlobalSettings;
 
     @VisibleForTesting
     protected boolean mUseHeadsUp = false;
@@ -111,7 +115,6 @@
 
     @Inject
     public NotificationInterruptStateProviderImpl(
-            ContentResolver contentResolver,
             PowerManager powerManager,
             AmbientDisplayConfiguration ambientDisplayConfiguration,
             BatteryController batteryController,
@@ -124,8 +127,9 @@
             KeyguardNotificationVisibilityProvider keyguardNotificationVisibilityProvider,
             UiEventLogger uiEventLogger,
             UserTracker userTracker,
-            DeviceProvisionedController deviceProvisionedController) {
-        mContentResolver = contentResolver;
+            DeviceProvisionedController deviceProvisionedController,
+            SystemClock systemClock,
+            GlobalSettings globalSettings) {
         mPowerManager = powerManager;
         mBatteryController = batteryController;
         mAmbientDisplayConfiguration = ambientDisplayConfiguration;
@@ -137,15 +141,16 @@
         mKeyguardNotificationVisibilityProvider = keyguardNotificationVisibilityProvider;
         mUiEventLogger = uiEventLogger;
         mUserTracker = userTracker;
+        mDeviceProvisionedController = deviceProvisionedController;
+        mSystemClock = systemClock;
+        mGlobalSettings = globalSettings;
         ContentObserver headsUpObserver = new ContentObserver(mainHandler) {
             @Override
             public void onChange(boolean selfChange) {
-                boolean wasUsing = mUseHeadsUp;
-                mUseHeadsUp = ENABLE_HEADS_UP
-                        && Settings.Global.HEADS_UP_OFF != Settings.Global.getInt(
-                        mContentResolver,
-                        Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
-                        Settings.Global.HEADS_UP_OFF);
+                final boolean wasUsing = mUseHeadsUp;
+                final boolean settingEnabled = HEADS_UP_OFF
+                        != mGlobalSettings.getInt(HEADS_UP_NOTIFICATIONS_ENABLED, HEADS_UP_OFF);
+                mUseHeadsUp = ENABLE_HEADS_UP && settingEnabled;
                 mLogger.logHeadsUpFeatureChanged(mUseHeadsUp);
                 if (wasUsing != mUseHeadsUp) {
                     if (!mUseHeadsUp) {
@@ -157,16 +162,15 @@
         };
 
         if (ENABLE_HEADS_UP) {
-            mContentResolver.registerContentObserver(
-                    Settings.Global.getUriFor(Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED),
+            mGlobalSettings.registerContentObserver(
+                    mGlobalSettings.getUriFor(HEADS_UP_NOTIFICATIONS_ENABLED),
                     true,
                     headsUpObserver);
-            mContentResolver.registerContentObserver(
-                    Settings.Global.getUriFor(SETTING_HEADS_UP_TICKER), true,
+            mGlobalSettings.registerContentObserver(
+                    mGlobalSettings.getUriFor(SETTING_HEADS_UP_TICKER), true,
                     headsUpObserver);
         }
         headsUpObserver.onChange(true); // set up
-        mDeviceProvisionedController = deviceProvisionedController;
     }
 
     @Override
@@ -603,7 +607,7 @@
         }
 
         final long when = notification.when;
-        final long now = System.currentTimeMillis();
+        final long now = mSystemClock.currentTimeMillis();
         final long age = now - when;
 
         if (age < MAX_HUN_WHEN_AGE_MS) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProvider.kt
index 920bbe9..7ead4bf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProvider.kt
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.android.systemui.statusbar.notification.interruption
 
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionRefactor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionRefactor.kt
new file mode 100644
index 0000000..2624363
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionRefactor.kt
@@ -0,0 +1,46 @@
+/*
+ * 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.notification.interruption
+
+import com.android.systemui.Flags
+import com.android.systemui.flags.RefactorFlagUtils
+
+/** Helper for reading or using the visual interruptions refactor flag state. */
+object VisualInterruptionRefactor {
+    const val FLAG_NAME = Flags.FLAG_VISUAL_INTERRUPTIONS_REFACTOR
+
+    /** Whether the refactor is enabled */
+    @JvmStatic
+    inline val isEnabled
+        get() = Flags.visualInterruptionsRefactor()
+
+    /**
+     * Called to ensure code is only run when the flag is enabled. This protects users from the
+     * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
+     * build to ensure that the refactor author catches issues in testing.
+     */
+    @JvmStatic
+    inline fun isUnexpectedlyInLegacyMode() =
+        RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
+
+    /**
+     * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+     * the flag is enabled to ensure that the refactor author catches issues in testing.
+     */
+    @JvmStatic
+    inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionSuppressor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionSuppressor.kt
index 4ef80e3..d524637 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionSuppressor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionSuppressor.kt
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.android.systemui.statusbar.notification.interruption
 
 import com.android.internal.logging.UiEventLogger.UiEventEnum
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryLogger.kt
index abe0670..c89f2fa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryLogger.kt
@@ -26,7 +26,7 @@
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.shared.system.SysUiStatsLog
 import com.android.systemui.statusbar.notification.collection.NotifPipeline
-import com.android.systemui.util.traceSection
+import com.android.systemui.tracing.traceSection
 import java.lang.Exception
 import java.util.concurrent.Executor
 import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
index 5c590870..a98efba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
@@ -31,7 +31,7 @@
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModel
 import com.android.systemui.statusbar.phone.NotificationIconAreaController
 import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.util.traceSection
+import com.android.systemui.tracing.traceSection
 
 /** Binds a [NotificationStackScrollLayout] to its [view model][NotificationListViewModel]. */
 object NotificationListViewBinder {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
index 6a24174..894549d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
@@ -26,9 +26,9 @@
 import androidx.annotation.VisibleForTesting
 import com.android.internal.policy.SystemBarUtils
 import com.android.systemui.Dumpable
-import com.android.systemui.res.R
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.res.R
 import com.android.systemui.statusbar.policy.CallbackController
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE
@@ -38,8 +38,7 @@
 import com.android.systemui.util.leak.RotationUtils.Rotation
 import com.android.systemui.util.leak.RotationUtils.getExactRotation
 import com.android.systemui.util.leak.RotationUtils.getResourcesForRotation
-import com.android.systemui.util.traceSection
-
+import com.android.systemui.tracing.traceSection
 import java.io.PrintWriter
 import java.lang.Math.max
 import javax.inject.Inject
@@ -134,8 +133,10 @@
      *      (i.e., ROTATION_NONE will always return the same bounds regardless of the context
      *      from which this method is called)
      */
-    fun getBoundingRectForPrivacyChipForRotation(@Rotation rotation: Int,
-                                                 displayCutout: DisplayCutout?): Rect {
+    fun getBoundingRectForPrivacyChipForRotation(
+        @Rotation rotation: Int,
+        displayCutout: DisplayCutout?
+    ): Rect {
         val key = getCacheKey(rotation, displayCutout)
         var insets = insetsCache[key]
         if (insets == null) {
@@ -276,7 +277,8 @@
 
     private fun getCacheKey(
             @Rotation rotation: Int,
-            displayCutout: DisplayCutout?): CacheKey =
+            displayCutout: DisplayCutout?
+        ): CacheKey =
         CacheKey(
             rotation = rotation,
             displaySize = Rect(context.resources.configuration.windowConfiguration.maxBounds),
@@ -411,7 +413,6 @@
     @Rotation targetRotation: Int,
     @Rotation currentRotation: Int
 ): Rect {
-
     val logicalDisplayWidth = if (targetRotation.isHorizontal()) height else width
 
     val cutoutRects = displayCutout?.boundingRects
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
index de37170..1eea348 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -31,7 +31,7 @@
 import com.android.systemui.statusbar.notification.stack.AnimationProperties
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator
 import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.util.TraceUtils
+import com.android.systemui.tracing.TraceUtils
 import com.android.systemui.util.settings.GlobalSettings
 import javax.inject.Inject
 import com.android.systemui.flags.FeatureFlags
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
index 3018e62..c3c0291 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
@@ -48,7 +48,7 @@
 import com.android.systemui.unfold.updates.RotationChangeProvider
 import com.android.systemui.unfold.util.ScaleAwareTransitionProgressProvider.Companion.areAnimationsEnabled
 import com.android.systemui.util.concurrency.ThreadFactory
-import com.android.systemui.util.traceSection
+import com.android.systemui.tracing.traceSection
 import com.android.wm.shell.displayareahelper.DisplayAreaHelper
 import java.util.Optional
 import java.util.concurrent.Executor
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt
index ed960f3..dfff7c4 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt
@@ -20,9 +20,9 @@
 import com.android.systemui.CoreStartable
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.tracing.TraceStateLogger
 import com.android.systemui.unfold.system.DeviceStateRepository
 import com.android.systemui.unfold.updates.FoldStateRepository
-import com.android.systemui.util.TraceStateLogger
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.launch
diff --git a/packages/SystemUI/src/com/android/systemui/util/NamedListenerSet.kt b/packages/SystemUI/src/com/android/systemui/util/NamedListenerSet.kt
index c90b57e..1e0f420 100644
--- a/packages/SystemUI/src/com/android/systemui/util/NamedListenerSet.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/NamedListenerSet.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.util
 
+import com.android.systemui.tracing.traceSection
 import java.util.concurrent.CopyOnWriteArrayList
 import java.util.function.Consumer
 
diff --git a/packages/SystemUI/src/com/android/systemui/util/NoRemeasureMotionLayout.kt b/packages/SystemUI/src/com/android/systemui/util/NoRemeasureMotionLayout.kt
index 3095d80..cec9580 100644
--- a/packages/SystemUI/src/com/android/systemui/util/NoRemeasureMotionLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/NoRemeasureMotionLayout.kt
@@ -20,6 +20,7 @@
 import android.util.AttributeSet
 import android.view.Choreographer
 import androidx.constraintlayout.motion.widget.MotionLayout
+import com.android.systemui.tracing.traceSection
 
 /**
  * [MotionLayout] that avoids remeasuring with the same inputs in the same frame.
diff --git a/packages/SystemUI/src/com/android/systemui/util/drawable/DrawableSize.kt b/packages/SystemUI/src/com/android/systemui/util/drawable/DrawableSize.kt
index b506808..afd2360 100644
--- a/packages/SystemUI/src/com/android/systemui/util/drawable/DrawableSize.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/drawable/DrawableSize.kt
@@ -13,7 +13,7 @@
 import android.graphics.drawable.Drawable
 import android.util.Log
 import androidx.annotation.Px
-import com.android.systemui.util.traceSection
+import com.android.systemui.tracing.traceSection
 
 class DrawableSize {
 
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt
index f0c7be6..5396bca 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt
@@ -7,8 +7,8 @@
 import com.android.systemui.dagger.qualifiers.Tracing
 import com.android.systemui.flags.FeatureFlagsClassic
 import com.android.systemui.flags.Flags
-import com.android.systemui.util.TraceUtils.Companion.coroutineTracingIsEnabled
-import com.android.systemui.util.tracing.TraceContextElement
+import com.android.systemui.tracing.TraceUtils.Companion.coroutineTracingIsEnabled
+import com.android.systemui.tracing.TraceContextElement
 import dagger.Module
 import dagger.Provides
 import kotlinx.coroutines.CoroutineDispatcher
diff --git a/packages/SystemUI/src/com/android/systemui/util/wrapper/LottieViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/util/wrapper/LottieViewWrapper.kt
index a804923..2e355bd 100644
--- a/packages/SystemUI/src/com/android/systemui/util/wrapper/LottieViewWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/wrapper/LottieViewWrapper.kt
@@ -18,7 +18,7 @@
 import android.content.Context
 import android.util.AttributeSet
 import com.airbnb.lottie.LottieAnimationView
-import com.android.systemui.util.traceSection
+import com.android.systemui.tracing.traceSection
 
 /** LottieAnimationView that traces each call to invalidate. */
 open class LottieViewWrapper : LottieAnimationView {
diff --git a/packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt b/packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt
index 374ebe0..8f320a3 100644
--- a/packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt
@@ -21,7 +21,7 @@
 import com.android.internal.view.RotationPolicy
 import com.android.internal.view.RotationPolicy.RotationPolicyListener
 import com.android.systemui.util.settings.SecureSettings
-import com.android.systemui.util.traceSection
+import com.android.systemui.tracing.traceSection
 import javax.inject.Inject
 
 /**
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
index 4bacc3d..adcec10 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
@@ -20,7 +20,6 @@
 
 import static com.android.keyguard.LockIconView.ICON_LOCK;
 import static com.android.keyguard.LockIconView.ICON_UNLOCK;
-import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.anyBoolean;
@@ -354,26 +353,8 @@
     }
 
     @Test
-    public void playHaptic_onTouchExploration_NoOneWayHaptics_usesVibrate() {
-        mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false);
-
-        // WHEN request to vibrate on touch exploration
-        mUnderTest.vibrateOnTouchExploration();
-
-        // THEN vibrates
-        verify(mVibrator).vibrate(
-                anyInt(),
-                any(),
-                eq(UdfpsController.EFFECT_CLICK),
-                eq("lock-icon-down"),
-                any());
-    }
-
-    @Test
-    public void playHaptic_onTouchExploration_withOneWayHaptics_performHapticFeedback() {
-        mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
-
-        // WHEN request to vibrate on touch exploration
+    public void playHaptic_onTouchExploration_performHapticFeedback() {
+       // WHEN request to vibrate on touch exploration
         mUnderTest.vibrateOnTouchExploration();
 
         // THEN performHapticFeedback is used
@@ -381,25 +362,7 @@
     }
 
     @Test
-    public void playHaptic_onLongPress_NoOneWayHaptics_usesVibrate() {
-        mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false);
-
-        // WHEN request to vibrate on long press
-        mUnderTest.vibrateOnLongPress();
-
-        // THEN uses vibrate
-        verify(mVibrator).vibrate(
-                anyInt(),
-                any(),
-                eq(UdfpsController.EFFECT_CLICK),
-                eq("lock-screen-lock-icon-longpress"),
-                any());
-    }
-
-    @Test
-    public void playHaptic_onLongPress_withOneWayHaptics_performHapticFeedback() {
-        mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
-
+    public void playHaptic_onLongPress_performHapticFeedback() {
         // WHEN request to vibrate on long press
         mUnderTest.vibrateOnLongPress();
 
@@ -411,7 +374,6 @@
     public void longPress_showBouncer_sceneContainerNotEnabled() {
         init(/* useMigrationFlag= */ false);
         mSceneTestUtils.getSceneContainerFlags().setEnabled(false);
-        mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
         when(mFalsingManager.isFalseLongTap(anyInt())).thenReturn(false);
 
         // WHEN longPress
@@ -426,7 +388,6 @@
     public void longPress_showBouncer() {
         init(/* useMigrationFlag= */ false);
         mSceneTestUtils.getSceneContainerFlags().setEnabled(true);
-        mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
         when(mFalsingManager.isFalseLongTap(anyInt())).thenReturn(false);
 
         // WHEN longPress
@@ -441,7 +402,6 @@
     public void longPress_falsingTriggered_doesNotShowBouncer() {
         init(/* useMigrationFlag= */ false);
         mSceneTestUtils.getSceneContainerFlags().setEnabled(true);
-        mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
         when(mFalsingManager.isFalseLongTap(anyInt())).thenReturn(true);
 
         // WHEN longPress
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index 06421db..044881e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -1072,6 +1072,7 @@
         assertTrue(TextUtils.equals(newA11yWindowTitle, getAccessibilityWindowTitle()));
     }
 
+    @Ignore("it's flaky in presubmit but works in abtd, filter for now. b/305654925")
     @Test
     public void onSingleTap_enabled_scaleAnimates() {
         mInstrumentation.runOnMainSync(() -> {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
index 834dccb..2a1cfd1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
@@ -93,7 +93,8 @@
         mViewPropertyAnimator = spy(mMenuView.animate());
         doReturn(mViewPropertyAnimator).when(mMenuView).animate();
 
-        mMenuAnimationController = new TestMenuAnimationController(mMenuView);
+        mMenuAnimationController = new TestMenuAnimationController(
+                mMenuView, stubMenuViewAppearance);
         mLastIsMoveToTucked = Prefs.getBoolean(mContext,
                 Prefs.Key.HAS_ACCESSIBILITY_FLOATING_MENU_TUCKED, /* defaultValue= */ false);
         mEndListenerCaptor = ArgumentCaptor.forClass(DynamicAnimation.OnAnimationEndListener.class);
@@ -277,8 +278,8 @@
      * Wrapper class for testing.
      */
     private static class TestMenuAnimationController extends MenuAnimationController {
-        TestMenuAnimationController(MenuView menuView) {
-            super(menuView);
+        TestMenuAnimationController(MenuView menuView, MenuViewAppearance menuViewAppearance) {
+            super(menuView, menuViewAppearance);
         }
 
         @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
index 4ac18d0..7f12c05 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
@@ -85,7 +85,8 @@
 
         doReturn(mDraggableBounds).when(mMenuView).getMenuDraggableBounds();
         mStubListView = new RecyclerView(mContext);
-        mMenuAnimationController = spy(new MenuAnimationController(mMenuView));
+        mMenuAnimationController = spy(new MenuAnimationController(mMenuView,
+                stubMenuViewAppearance));
         mMenuItemAccessibilityDelegate =
                 new MenuItemAccessibilityDelegate(new RecyclerViewAccessibilityDelegate(
                         mStubListView), mMenuAnimationController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
index f0a497d..9797f2a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
@@ -88,7 +88,8 @@
         mStubMenuView = new MenuView(mContext, stubMenuViewModel, stubMenuViewAppearance);
         mStubMenuView.setTranslationX(0);
         mStubMenuView.setTranslationY(0);
-        mMenuAnimationController = spy(new MenuAnimationController(mStubMenuView));
+        mMenuAnimationController = spy(new MenuAnimationController(
+                mStubMenuView, stubMenuViewAppearance));
         mDismissView = spy(new DismissView(mContext));
         DismissViewUtils.setup(mDismissView);
         mDismissAnimationController =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
index 5cd0fd0..8f0a97c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
@@ -27,6 +27,9 @@
 import android.app.UiModeManager;
 import android.graphics.Rect;
 import android.graphics.drawable.GradientDrawable;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.WindowManager;
@@ -34,6 +37,7 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.systemui.Flags;
 import com.android.systemui.Prefs;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.util.settings.SecureSettings;
@@ -62,6 +66,10 @@
     @Rule
     public MockitoRule mockito = MockitoJUnit.rule();
 
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule =
+            DeviceFlagsValueProvider.createCheckFlagsRule();
+
     @Mock
     private AccessibilityManager mAccessibilityManager;
 
@@ -138,6 +146,22 @@
         assertThat(radii[7]).isEqualTo(0.0f);
     }
 
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_FLOATING_MENU_RADII_ANIMATION)
+    public void onEdgeChanged_startsRadiiAnimation() {
+        final RadiiAnimator radiiAnimator = getRadiiAnimator();
+        mMenuView.onEdgeChanged();
+        assertThat(radiiAnimator.isStarted()).isTrue();
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_FLOATING_MENU_RADII_ANIMATION)
+    public void onDraggingStart_startsRadiiAnimation() {
+        final RadiiAnimator radiiAnimator = getRadiiAnimator();
+        mMenuView.onDraggingStart();
+        assertThat(radiiAnimator.isStarted()).isTrue();
+    }
+
     private InstantInsetLayerDrawable getMenuViewInsetLayer() {
         return (InstantInsetLayerDrawable) mMenuView.getBackground();
     }
@@ -146,6 +170,15 @@
         return (GradientDrawable) getMenuViewInsetLayer().getDrawable(INDEX_MENU_ITEM);
     }
 
+    private RadiiAnimator getRadiiAnimator() {
+        final RadiiAnimator radiiAnimator = mMenuView.getMenuAnimationController().mRadiiAnimator;
+        if (radiiAnimator.isStarted()) {
+            radiiAnimator.skipAnimationToEnd();
+        }
+        assertThat(radiiAnimator.isStarted()).isFalse();
+        return radiiAnimator;
+    }
+
     @After
     public void tearDown() throws Exception {
         mUiModeManager.setNightMode(mNightMode);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/RadiiAnimatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/RadiiAnimatorTest.java
new file mode 100644
index 0000000..e3a2c59
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/RadiiAnimatorTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility.floatingmenu;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.accessibility.utils.TestUtils;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+
+/** Tests for {@link RadiiAnimator}. */
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class RadiiAnimatorTest extends SysuiTestCase {
+    float[] mResultRadii = new float[RadiiAnimator.RADII_COUNT];
+
+    @Test
+    public void constructor() {
+        final float[] radii = generateRadii(0.0f);
+        final RadiiAnimator radiiAnimator = new RadiiAnimator(radii, newRadii -> {});
+
+        assertThat(radiiAnimator.evaluate(0.0f)).isEqualTo(radii);
+    }
+
+    @Test
+    public void skip_updates_to_end() {
+        final float[] startRadii = generateRadii(0.0f);
+        final float[] endRadii = generateRadii(1.0f);
+
+        final RadiiAnimator radiiAnimator = setupAnimator(startRadii);
+
+        new Handler(Looper.getMainLooper()).post(() -> radiiAnimator.startAnimation(endRadii));
+        TestUtils.waitForCondition(radiiAnimator::isStarted, "Animation did not start.");
+        TestUtils.waitForCondition(() -> Arrays.equals(radiiAnimator.evaluate(0.0f), startRadii)
+                                && Arrays.equals(radiiAnimator.evaluate(1.0f), endRadii),
+                "Animator did not initialize to start and end values");
+
+        new Handler(Looper.getMainLooper()).post(radiiAnimator::skipAnimationToEnd);
+        TestUtils.waitForCondition(
+                () -> !radiiAnimator.isStarted(), "Animation did not end.");
+        assertThat(mResultRadii).usingTolerance(0.001).containsExactly(endRadii);
+    }
+
+    @Test
+    public void animation_can_repeat() {
+        final float[] startRadii = generateRadii(0.0f);
+        final float[] midRadii = generateRadii(1.0f);
+        final float[] endRadii = generateRadii(2.0f);
+
+        final RadiiAnimator radiiAnimator = setupAnimator(startRadii);
+
+        playAndSkipAnimation(radiiAnimator, midRadii);
+        assertThat(mResultRadii).usingTolerance(0.001).containsExactly(midRadii);
+
+        playAndSkipAnimation(radiiAnimator, endRadii);
+        assertThat(mResultRadii).usingTolerance(0.001).containsExactly(endRadii);
+    }
+
+    private float[] generateRadii(float value) {
+        float[] radii = new float[8];
+        Arrays.fill(radii, value);
+        return radii;
+    }
+
+    private RadiiAnimator setupAnimator(float[] startRadii) {
+        mResultRadii = new float[RadiiAnimator.RADII_COUNT];
+        return new RadiiAnimator(startRadii,
+                newRadii -> mResultRadii = newRadii);
+    }
+
+    private void playAndSkipAnimation(RadiiAnimator animator, float[] endRadii) {
+        new Handler(Looper.getMainLooper()).post(() -> animator.startAnimation(endRadii));
+        TestUtils.waitForCondition(animator::isStarted, "Animation did not start.");
+        new Handler(Looper.getMainLooper()).post(animator::skipAnimationToEnd);
+        TestUtils.waitForCondition(
+                () -> !animator.isStarted(), "Animation did not end.");
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/utils/TestUtils.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/utils/TestUtils.java
new file mode 100644
index 0000000..10c8caa
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/utils/TestUtils.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility.utils;
+
+import android.os.SystemClock;
+
+import java.util.function.BooleanSupplier;
+
+public class TestUtils {
+    public static long DEFAULT_CONDITION_DURATION = 5_000;
+
+    /**
+     * Waits an amount of time specified by {@link TestUtils#DEFAULT_CONDITION_DURATION}
+     * for a condition to become true.
+     * On failure, throws a {@link RuntimeException} with a custom message.
+     *
+     * @param c Condition which must return true to proceed.
+     * @param message Message to print on failure.
+     */
+    public static void waitForCondition(BooleanSupplier condition, String message) {
+        waitForCondition(condition, message, DEFAULT_CONDITION_DURATION);
+    }
+
+    /**
+     * Waits up to a specified amount of time for a condition to become true.
+     * On failure, throws a {@link RuntimeException} with a custom message.
+     *
+     * @param c Condition which must return true to proceed.
+     * @param message Message to print on failure.
+     * @param duration Amount of time permitted to wait.
+     */
+    public static void waitForCondition(BooleanSupplier condition, String message, long duration) {
+        long deadline = SystemClock.uptimeMillis() + duration;
+        long sleepMs = 50;
+        while (!condition.getAsBoolean()) {
+            if (SystemClock.uptimeMillis() > deadline) {
+                throw new RuntimeException(message);
+            }
+            // Reduce frequency of checks as more checks occur
+            sleepMs *= 2;
+            SystemClock.sleep(sleepMs);
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index f899e2f..5e7b857 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -505,7 +505,6 @@
                 this.authenticators = authenticators
             }
         },
-        featureFlags,
         testScope.backgroundScope,
         fingerprintProps,
         faceProps,
@@ -519,9 +518,7 @@
         PromptViewModel(
             displayStateInteractor,
             promptSelectorInteractor,
-            vibrator,
             context,
-            featureFlags
         ),
         { credentialViewModel },
         Handler(TestableLooper.get(this).looper),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index 885abcb..11c5d3b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -88,7 +88,6 @@
 import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor;
 import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel;
 import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel;
-import com.android.systemui.flags.FakeFeatureFlags;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.VibratorHelper;
@@ -195,7 +194,6 @@
     private Handler mHandler;
     private DelayableExecutor mBackgroundExecutor;
     private TestableAuthController mAuthController;
-    private FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
 
     @Before
     public void setup() throws RemoteException {
@@ -1023,7 +1021,7 @@
         private PromptInfo mLastBiometricPromptInfo;
 
         TestableAuthController(Context context) {
-            super(context, mFeatureFlags, null /* applicationCoroutineScope */,
+            super(context, null /* applicationCoroutineScope */,
                     mExecution, mCommandQueue, mActivityTaskManager, mWindowManager,
                     mFingerprintManager, mFaceManager, () -> mUdfpsController,
                     () -> mSideFpsController, mDisplayManager, mWakefulnessLifecycle,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
index b695a0e..d06cbbb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
@@ -44,18 +44,16 @@
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
 import com.android.systemui.display.data.repository.FakeDisplayRepository
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.any
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceUntilIdle
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
@@ -64,9 +62,7 @@
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
 import org.mockito.Mock
-import org.mockito.Mockito.never
 import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
 import org.mockito.junit.MockitoJUnit
 
 private const val USER_ID = 4
@@ -95,7 +91,6 @@
     private lateinit var selector: PromptSelectorInteractor
     private lateinit var viewModel: PromptViewModel
     private lateinit var iconViewModel: PromptIconViewModel
-    private val featureFlags = FakeFeatureFlags()
 
     @Before
     fun setup() {
@@ -125,10 +120,8 @@
             PromptSelectorInteractorImpl(fingerprintRepository, promptRepository, lockPatternUtils)
         selector.resetPrompt()
 
-        viewModel =
-            PromptViewModel(displayStateInteractor, selector, vibrator, mContext, featureFlags)
+        viewModel = PromptViewModel(displayStateInteractor, selector, mContext)
         iconViewModel = viewModel.iconViewModel
-        featureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false)
     }
 
     @Test
@@ -180,26 +173,29 @@
     }
 
     @Test
-    fun play_haptic_on_confirm_when_confirmation_required_otherwise_on_authenticated() =
+    fun set_haptic_on_confirm_when_confirmation_required_otherwise_on_authenticated() =
         runGenericTest {
             val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)
 
             viewModel.showAuthenticated(testCase.authenticatedModality, 1_000L)
 
-            verify(vibrator, if (expectConfirmation) never() else times(1))
-                .vibrateAuthSuccess(any())
+            val confirmConstant by collectLastValue(viewModel.hapticsToPlay)
+            assertThat(confirmConstant)
+                .isEqualTo(
+                    if (expectConfirmation) HapticFeedbackConstants.NO_HAPTICS
+                    else HapticFeedbackConstants.CONFIRM
+                )
 
             if (expectConfirmation) {
                 viewModel.confirmAuthenticated()
             }
 
-            verify(vibrator).vibrateAuthSuccess(any())
-            verify(vibrator, never()).vibrateAuthError(any())
+            val confirmedConstant by collectLastValue(viewModel.hapticsToPlay)
+            assertThat(confirmedConstant).isEqualTo(HapticFeedbackConstants.CONFIRM)
         }
 
     @Test
-    fun playSuccessHaptic_onwayHapticsEnabled_SetsConfirmConstant() = runGenericTest {
-        featureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true)
+    fun playSuccessHaptic_SetsConfirmConstant() = runGenericTest {
         val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)
         viewModel.showAuthenticated(testCase.authenticatedModality, 1_000L)
 
@@ -212,8 +208,7 @@
     }
 
     @Test
-    fun playErrorHaptic_onwayHapticsEnabled_SetsRejectConstant() = runGenericTest {
-        featureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true)
+    fun playErrorHaptic_SetsRejectConstant() = runGenericTest {
         viewModel.showTemporaryError("test", "messageAfterError", false)
 
         val currentConstant by collectLastValue(viewModel.hapticsToPlay)
@@ -251,7 +246,8 @@
                             DisplayRotation.ROTATION_180 ->
                                 R.raw.biometricprompt_fingerprint_to_error_landscape
                             DisplayRotation.ROTATION_270 ->
-                                R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_bottomright
+                                R.raw
+                                    .biometricprompt_symbol_fingerprint_to_error_portrait_bottomright
                             else -> throw Exception("invalid rotation")
                         }
                     assertThat(iconOverlayAsset).isEqualTo(expectedOverlayAsset)
@@ -496,7 +492,8 @@
                             DisplayRotation.ROTATION_180 ->
                                 R.raw.biometricprompt_symbol_fingerprint_to_success_landscape
                             DisplayRotation.ROTATION_270 ->
-                                R.raw.biometricprompt_symbol_fingerprint_to_success_portrait_bottomright
+                                R.raw
+                                    .biometricprompt_symbol_fingerprint_to_success_portrait_bottomright
                             else -> throw Exception("invalid rotation")
                         }
                     assertThat(iconOverlayAsset).isEqualTo(expectedOverlayAsset)
@@ -733,7 +730,7 @@
     }
 
     @Test
-    fun plays_haptic_on_errors() = runGenericTest {
+    fun set_haptic_on_errors() = runGenericTest {
         viewModel.showTemporaryError(
             "so sad",
             messageAfterError = "",
@@ -741,8 +738,8 @@
             hapticFeedback = true,
         )
 
-        verify(vibrator).vibrateAuthError(any())
-        verify(vibrator, never()).vibrateAuthSuccess(any())
+        val constant by collectLastValue(viewModel.hapticsToPlay)
+        assertThat(constant).isEqualTo(HapticFeedbackConstants.REJECT)
     }
 
     @Test
@@ -754,8 +751,8 @@
             hapticFeedback = false,
         )
 
-        verify(vibrator, never()).vibrateAuthError(any())
-        verify(vibrator, never()).vibrateAuthSuccess(any())
+        val constant by collectLastValue(viewModel.hapticsToPlay)
+        assertThat(constant).isEqualTo(HapticFeedbackConstants.NO_HAPTICS)
     }
 
     private suspend fun TestScope.showTemporaryErrors(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
index 80fe9e7..fcb18f5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
@@ -26,6 +26,7 @@
 import static org.mockito.Mockito.when;
 
 import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
 import android.view.MotionEvent;
 
 import androidx.test.filters.SmallTest;
@@ -35,13 +36,14 @@
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.dock.DockManagerFake;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shade.ShadeExpansionStateManager;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
 import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.kotlin.JavaAdapter;
 import com.android.systemui.util.sensors.ProximitySensor;
 import com.android.systemui.util.sensors.ThresholdSensor;
 import com.android.systemui.util.time.FakeSystemClock;
@@ -54,8 +56,11 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import kotlinx.coroutines.flow.StateFlowKt;
+
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class FalsingCollectorImplTest extends SysuiTestCase {
 
     private FalsingCollectorImpl mFalsingCollector;
@@ -73,7 +78,9 @@
     @Mock
     private KeyguardStateController mKeyguardStateController;
     @Mock
-    private ShadeExpansionStateManager mShadeExpansionStateManager;
+    private ShadeInteractor mShadeInteractor;
+    @Mock
+    private JavaAdapter mJavaAdapter;
     @Mock
     private BatteryController mBatteryController;
     @Mock
@@ -88,12 +95,16 @@
 
         when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
         when(mKeyguardStateController.isShowing()).thenReturn(true);
+        when(mShadeInteractor.isQsExpanded()).thenReturn(StateFlowKt.MutableStateFlow(false));
 
         mFalsingCollector = new FalsingCollectorImpl(mFalsingDataProvider, mFalsingManager,
                 mKeyguardUpdateMonitor, mHistoryTracker, mProximitySensor,
-                mStatusBarStateController, mKeyguardStateController, mShadeExpansionStateManager,
-                mBatteryController,
-                mDockManager, mFakeExecutor, mFakeSystemClock, () -> mSelectedUserInteractor);
+                mStatusBarStateController, mKeyguardStateController,
+                () -> mShadeInteractor, mBatteryController,
+                mDockManager, mFakeExecutor,
+                mJavaAdapter, mFakeSystemClock, () -> mSelectedUserInteractor
+        );
+        mFalsingCollector.init();
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
index 3af444a..27fd3b1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
@@ -165,12 +165,28 @@
         int maxBrightness = 3;
         when(mSystemSettings.getIntForUser(eq(Settings.System.SCREEN_BRIGHTNESS), anyInt(),
                 eq(UserHandle.USER_CURRENT))).thenReturn(maxBrightness);
+        when(mSystemSettings.getIntForUser(eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
+                eq(UserHandle.USER_CURRENT)))
+                .thenReturn(Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
 
         mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
         assertEquals(maxBrightness, mServiceFake.screenBrightness);
     }
 
     @Test
+    public void testAod_usesLightSensorNotClampingToAutoBrightnessValue() {
+        int maxBrightness = 3;
+        when(mSystemSettings.getIntForUser(eq(Settings.System.SCREEN_BRIGHTNESS), anyInt(),
+                eq(UserHandle.USER_CURRENT))).thenReturn(maxBrightness);
+        when(mSystemSettings.getIntForUser(eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
+                eq(UserHandle.USER_CURRENT)))
+                .thenReturn(Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+
+        mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+        assertEquals(DEFAULT_BRIGHTNESS, mServiceFake.screenBrightness);
+    }
+
+    @Test
     public void doze_doesNotUseLightSensor() {
         // GIVEN the device is DOZE and the display state changes to ON
         mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 2b280c0..814a317 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -253,7 +253,6 @@
                 mKeyguardStateController,
                 mScreenOffAnimationController,
                 mAuthController,
-                mShadeExpansionStateManager,
                 () -> mShadeInteractor,
                 mShadeWindowLogger,
                 () -> mSelectedUserInteractor);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 2ce4b04..446a0b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -99,7 +99,6 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
-import com.android.systemui.keyguard.ui.view.KeyguardRootView;
 import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingLockscreenHostedTransitionViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel;
@@ -148,7 +147,6 @@
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.stack.AmbientState;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
-import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
 import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator;
@@ -335,7 +333,6 @@
     @Mock protected KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
     @Mock private JavaAdapter mJavaAdapter;
     @Mock private CastController mCastController;
-    @Mock private KeyguardRootView mKeyguardRootView;
     @Mock private SharedNotificationContainerInteractor mSharedNotificationContainerInteractor;
     @Mock private KeyguardClockPositionAlgorithm mKeyguardClockPositionAlgorithm;
 
@@ -575,14 +572,13 @@
         PulseExpansionHandler expansionHandler = new PulseExpansionHandler(
                 mContext,
                 coordinator,
-                mKeyguardBypassController, mHeadsUpManager,
-                mock(NotificationRoundnessManager.class),
+                mKeyguardBypassController,
+                mHeadsUpManager,
                 mConfigurationController,
                 mStatusBarStateController,
                 mFalsingManager,
-                mShadeExpansionStateManager,
+                mShadeInteractor,
                 mLockscreenShadeTransitionController,
-                new FalsingCollectorFake(),
                 mDumpManager);
         when(mKeyguardStatusViewComponentFactory.build(any(), any()))
                 .thenReturn(mKeyguardStatusViewComponent);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
index 4ba850c..8e0cf7d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -254,7 +254,6 @@
                 mKeyguardStateController,
                 mScreenOffAnimationController,
                 mAuthController,
-                mShadeExpansionStateManager,
                 () -> mShadeInteractor,
                 mShadeWindowLogger,
                 () -> mSelectedUserInteractor) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
index e70dbc7..778cfa6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
@@ -18,7 +18,6 @@
 
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
-import android.testing.TestableResources
 import android.view.View
 import android.view.ViewGroup
 import android.view.WindowInsets
@@ -27,7 +26,6 @@
 import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.constraintlayout.widget.ConstraintSet
 import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.flags.FakeFeatureFlags
 import com.android.systemui.flags.Flags
@@ -38,6 +36,8 @@
 import com.android.systemui.plugins.qs.QS
 import com.android.systemui.recents.OverviewProxyService
 import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener
+import com.android.systemui.res.R
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
 import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
 import com.android.systemui.util.concurrency.FakeExecutor
@@ -46,6 +46,7 @@
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import java.util.function.Consumer
+import kotlinx.coroutines.flow.MutableStateFlow
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -66,7 +67,7 @@
 
 /** Uses Flags.MIGRATE_NSSL set to false. If all goes well, this set of tests will be deleted. */
 @RunWith(AndroidTestingRunner::class)
-@TestableLooper.RunWithLooper
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() {
 
@@ -74,7 +75,7 @@
     @Mock lateinit var navigationModeController: NavigationModeController
     @Mock lateinit var overviewProxyService: OverviewProxyService
     @Mock lateinit var shadeHeaderController: ShadeHeaderController
-    @Mock lateinit var shadeExpansionStateManager: ShadeExpansionStateManager
+    @Mock lateinit var shadeInteractor: ShadeInteractor
     @Mock lateinit var fragmentService: FragmentService
     @Mock lateinit var fragmentHostManager: FragmentHostManager
     @Mock
@@ -88,7 +89,6 @@
 
     lateinit var underTest: NotificationsQSContainerController
 
-    private lateinit var fakeResources: TestableResources
     private lateinit var featureFlags: FakeFeatureFlags
     private lateinit var navigationModeCallback: ModeChangedListener
     private lateinit var taskbarVisibilityCallback: OverviewProxyListener
@@ -111,6 +111,7 @@
         whenever(view.resources).thenReturn(mContext.resources)
 
         whenever(fragmentService.getFragmentHostManager(any())).thenReturn(fragmentHostManager)
+        whenever(shadeInteractor.isQsExpanded).thenReturn(MutableStateFlow(false))
 
         underTest =
             NotificationsQSContainerController(
@@ -118,7 +119,7 @@
                 navigationModeController,
                 overviewProxyService,
                 shadeHeaderController,
-                shadeExpansionStateManager,
+                shadeInteractor,
                 fragmentService,
                 delayableExecutor,
                 featureFlags,
@@ -475,7 +476,7 @@
                 navigationModeController,
                 overviewProxyService,
                 shadeHeaderController,
-                shadeExpansionStateManager,
+                shadeInteractor,
                 fragmentService,
                 delayableExecutor,
                 featureFlags,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
index ac8c924..2342003 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
@@ -18,7 +18,6 @@
 
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
-import android.testing.TestableResources
 import android.view.View
 import android.view.ViewGroup
 import android.view.WindowInsets
@@ -27,7 +26,6 @@
 import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.constraintlayout.widget.ConstraintSet
 import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.flags.FakeFeatureFlags
 import com.android.systemui.flags.Flags
@@ -38,6 +36,8 @@
 import com.android.systemui.plugins.qs.QS
 import com.android.systemui.recents.OverviewProxyService
 import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener
+import com.android.systemui.res.R
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
 import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
 import com.android.systemui.util.concurrency.FakeExecutor
@@ -46,6 +46,7 @@
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import java.util.function.Consumer
+import kotlinx.coroutines.flow.MutableStateFlow
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -65,7 +66,7 @@
 import org.mockito.MockitoAnnotations
 
 @RunWith(AndroidTestingRunner::class)
-@TestableLooper.RunWithLooper
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 class NotificationsQSContainerControllerTest : SysuiTestCase() {
 
@@ -73,7 +74,7 @@
     @Mock lateinit var navigationModeController: NavigationModeController
     @Mock lateinit var overviewProxyService: OverviewProxyService
     @Mock lateinit var shadeHeaderController: ShadeHeaderController
-    @Mock lateinit var shadeExpansionStateManager: ShadeExpansionStateManager
+    @Mock lateinit var shadeInteractor: ShadeInteractor
     @Mock lateinit var fragmentService: FragmentService
     @Mock lateinit var fragmentHostManager: FragmentHostManager
     @Mock
@@ -87,7 +88,6 @@
 
     lateinit var underTest: NotificationsQSContainerController
 
-    private lateinit var fakeResources: TestableResources
     private lateinit var featureFlags: FakeFeatureFlags
     private lateinit var navigationModeCallback: ModeChangedListener
     private lateinit var taskbarVisibilityCallback: OverviewProxyListener
@@ -111,13 +111,15 @@
 
         whenever(fragmentService.getFragmentHostManager(any())).thenReturn(fragmentHostManager)
 
+        whenever(shadeInteractor.isQsExpanded).thenReturn(MutableStateFlow(false))
+
         underTest =
             NotificationsQSContainerController(
                 view,
                 navigationModeController,
                 overviewProxyService,
                 shadeHeaderController,
-                shadeExpansionStateManager,
+                shadeInteractor,
                 fragmentService,
                 delayableExecutor,
                 featureFlags,
@@ -458,7 +460,7 @@
                 navigationModeController,
                 overviewProxyService,
                 shadeHeaderController,
-                shadeExpansionStateManager,
+                shadeInteractor,
                 fragmentService,
                 delayableExecutor,
                 featureFlags,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
index 6eeafefd..e920687 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
@@ -189,4 +189,13 @@
             underTest.setUdfpsTransitionToFullShadeProgress(1f)
             assertThat(underTest.udfpsTransitionToFullShadeProgress.value).isEqualTo(1f)
         }
+
+    @Test
+    fun updateLegacyIsQsExpanded() =
+        testScope.runTest {
+            assertThat(underTest.legacyIsQsExpanded.value).isEqualTo(false)
+
+            underTest.setLegacyIsQsExpanded(true)
+            assertThat(underTest.legacyIsQsExpanded.value).isEqualTo(true)
+        }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt
index 20e5c43..49e5c45 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt
@@ -21,18 +21,17 @@
 import android.testing.TestableLooper
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.shade.ShadeExpansionStateManager
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
 import com.android.systemui.statusbar.notification.row.ExpandableView
-import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager
 import com.android.systemui.statusbar.phone.KeyguardBypassController
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.statusbar.policy.HeadsUpManager
 import com.android.systemui.util.mockito.mock
+import kotlinx.coroutines.flow.MutableStateFlow
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -53,19 +52,18 @@
     private val wakeUpCoordinator: NotificationWakeUpCoordinator = mock()
     private val bypassController: KeyguardBypassController = mock()
     private val headsUpManager: HeadsUpManager = mock()
-    private val roundnessManager: NotificationRoundnessManager = mock()
     private val configurationController: ConfigurationController = mock()
     private val statusBarStateController: StatusBarStateController = mock()
     private val falsingManager: FalsingManager = mock()
-    private val shadeExpansionStateManager: ShadeExpansionStateManager = mock()
+    private val shadeInteractor: ShadeInteractor = mock()
     private val lockscreenShadeTransitionController: LockscreenShadeTransitionController = mock()
-    private val falsingCollector: FalsingCollector = mock()
     private val dumpManager: DumpManager = mock()
     private val expandableView: ExpandableView = mock()
 
     @Before
     fun setUp() {
         whenever(expandableView.collapsedHeight).thenReturn(collapsedHeight)
+        whenever(shadeInteractor.isQsExpanded).thenReturn(MutableStateFlow(false))
 
         pulseExpansionHandler =
             PulseExpansionHandler(
@@ -73,13 +71,11 @@
                 wakeUpCoordinator,
                 bypassController,
                 headsUpManager,
-                roundnessManager,
                 configurationController,
                 statusBarStateController,
                 falsingManager,
-                shadeExpansionStateManager,
+                shadeInteractor,
                 lockscreenShadeTransitionController,
-                falsingCollector,
                 dumpManager
             )
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt
index 2e223f6..df257ab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt
@@ -27,7 +27,6 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.AnimatorTestRule
-import com.android.systemui.flags.FakeFeatureFlags
 import com.android.systemui.statusbar.phone.StatusBarContentInsetsChangedListener
 import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
 import com.android.systemui.statusbar.window.StatusBarWindowController
@@ -88,8 +87,7 @@
             SystemEventChipAnimationController(
                 context = mContext,
                 statusBarWindowController = sbWindowController,
-                contentInsetsProvider = insetsProvider,
-                featureFlags = FakeFeatureFlags(),
+                contentInsetsProvider = insetsProvider
             )
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventCoordinatorTest.kt
index c289ff3..bbc63f2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventCoordinatorTest.kt
@@ -21,7 +21,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor
 import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor.PendingDisplay
-import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor.State.CONNECTED
 import com.android.systemui.privacy.PrivacyItemController
 import com.android.systemui.statusbar.policy.BatteryController
 import com.android.systemui.util.mockito.any
@@ -48,7 +48,6 @@
 class SystemEventCoordinatorTest : SysuiTestCase() {
 
     private val fakeSystemClock = FakeSystemClock()
-    private val featureFlags = FakeFeatureFlags()
     private val testScope = TestScope(UnconfinedTestDispatcher())
     private val connectedDisplayInteractor = FakeConnectedDisplayInteractor()
 
@@ -66,7 +65,6 @@
                     batteryController,
                     privacyController,
                     context,
-                    featureFlags,
                     TestScope(UnconfinedTestDispatcher()),
                     connectedDisplayInteractor
                 )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt
index fee8b82..5f01b5a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt
@@ -26,8 +26,6 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.AnimatorTestRule
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.privacy.OngoingPrivacyChip
 import com.android.systemui.statusbar.BatteryStatusChip
 import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
@@ -76,7 +74,6 @@
     private lateinit var systemClock: FakeSystemClock
     private lateinit var chipAnimationController: SystemEventChipAnimationController
     private lateinit var systemStatusAnimationScheduler: SystemStatusAnimationScheduler
-    private val fakeFeatureFlags = FakeFeatureFlags()
 
     @get:Rule val animatorTestRule = AnimatorTestRule()
 
@@ -84,15 +81,12 @@
     fun setup() {
         MockitoAnnotations.initMocks(this)
 
-        fakeFeatureFlags.set(Flags.PLUG_IN_STATUS_BAR_CHIP, true)
-
         systemClock = FakeSystemClock()
         chipAnimationController =
             SystemEventChipAnimationController(
                 mContext,
                 statusBarWindowController,
-                statusBarContentInsetProvider,
-                fakeFeatureFlags
+                statusBarContentInsetProvider
             )
 
         // StatusBarContentInsetProvider is mocked. Ensure that it returns some mocked values.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
index 50ce265..1c62161 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
@@ -13,8 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.systemui.statusbar.notification.interruption;
 
+package com.android.systemui.statusbar.notification.interruption;
 
 import static android.app.Notification.FLAG_BUBBLE;
 import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
@@ -27,6 +27,8 @@
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
 import static android.app.NotificationManager.VISIBILITY_NO_OVERRIDE;
+import static android.provider.Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED;
+import static android.provider.Settings.Global.HEADS_UP_ON;
 
 import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
@@ -61,9 +63,9 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.logging.testing.UiEventLoggerFake;
-import com.android.systemui.res.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.res.R;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.notification.NotifPipelineFlags;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -74,6 +76,8 @@
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.settings.FakeGlobalSettings;
+import com.android.systemui.util.time.FakeSystemClock;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -120,6 +124,8 @@
     UserTracker mUserTracker;
     @Mock
     DeviceProvisionedController mDeviceProvisionedController;
+    FakeSystemClock mSystemClock;
+    FakeGlobalSettings mGlobalSettings;
 
     private NotificationInterruptStateProviderImpl mNotifInterruptionStateProvider;
 
@@ -129,10 +135,12 @@
         when(mUserTracker.getUserId()).thenReturn(ActivityManager.getCurrentUser());
 
         mUiEventLoggerFake = new UiEventLoggerFake();
+        mSystemClock = new FakeSystemClock();
+        mGlobalSettings = new FakeGlobalSettings();
+        mGlobalSettings.putInt(HEADS_UP_NOTIFICATIONS_ENABLED, HEADS_UP_ON);
 
         mNotifInterruptionStateProvider =
                 new NotificationInterruptStateProviderImpl(
-                        mContext.getContentResolver(),
                         mPowerManager,
                         mAmbientDisplayConfiguration,
                         mBatteryController,
@@ -145,7 +153,9 @@
                         mKeyguardNotificationVisibilityProvider,
                         mUiEventLoggerFake,
                         mUserTracker,
-                        mDeviceProvisionedController);
+                        mDeviceProvisionedController,
+                        mSystemClock,
+                        mGlobalSettings);
         mNotifInterruptionStateProvider.mUseHeadsUp = true;
     }
 
@@ -426,7 +436,7 @@
     }
 
     private long makeWhenHoursAgo(long hoursAgo) {
-        return System.currentTimeMillis() - (1000 * 60 * 60 * hoursAgo);
+        return mSystemClock.currentTimeMillis() - (1000 * 60 * 60 * hoursAgo);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt
index 947bcfb..21de73a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt
@@ -1,3 +1,19 @@
+/*
+ * 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.notification.interruption
 
 import android.testing.AndroidTestingRunner
@@ -19,27 +35,28 @@
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
 class NotificationInterruptStateProviderWrapperTest : VisualInterruptionDecisionProviderTestBase() {
-    override val provider: VisualInterruptionDecisionProvider
-        get() =
-            NotificationInterruptStateProviderWrapper(
-                NotificationInterruptStateProviderImpl(
-                        context.contentResolver,
-                        powerManager,
-                        ambientDisplayConfiguration,
-                        batteryController,
-                        statusBarStateController,
-                        keyguardStateController,
-                        headsUpManager,
-                        logger,
-                        mainHandler,
-                        flags,
-                        keyguardNotificationVisibilityProvider,
-                        uiEventLogger,
-                        userTracker,
-                        deviceProvisionedController
-                    )
-                    .also { it.mUseHeadsUp = true }
-            )
+    override val provider by lazy {
+        NotificationInterruptStateProviderWrapper(
+            NotificationInterruptStateProviderImpl(
+                    powerManager,
+                    ambientDisplayConfiguration,
+                    batteryController,
+                    statusBarStateController,
+                    keyguardStateController,
+                    headsUpManager,
+                    logger,
+                    mainHandler,
+                    flags,
+                    keyguardNotificationVisibilityProvider,
+                    uiEventLogger,
+                    userTracker,
+                    deviceProvisionedController,
+                    systemClock,
+                    globalSettings,
+                )
+                .also { it.mUseHeadsUp = true }
+        )
+    }
 
     // Tests of internals of the wrapper:
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
index 6f4bbd5..c0aaa36 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
@@ -1,3 +1,19 @@
+/*
+ * 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.notification.interruption
 
 import android.app.ActivityManager
@@ -9,12 +25,15 @@
 import android.app.NotificationManager.VISIBILITY_NO_OVERRIDE
 import android.app.PendingIntent
 import android.app.PendingIntent.FLAG_MUTABLE
+import android.content.Context
 import android.content.Intent
 import android.content.pm.UserInfo
 import android.graphics.drawable.Icon
 import android.hardware.display.FakeAmbientDisplayConfiguration
 import android.os.Handler
 import android.os.PowerManager
+import android.provider.Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED
+import android.provider.Settings.Global.HEADS_UP_ON
 import com.android.internal.logging.testing.UiEventLoggerFake
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.res.R
@@ -31,8 +50,11 @@
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.settings.FakeGlobalSettings
+import com.android.systemui.util.time.FakeSystemClock
 import com.android.systemui.utils.leaks.FakeBatteryController
 import com.android.systemui.utils.leaks.LeakCheckedTest
+import junit.framework.Assert.assertFalse
 import junit.framework.Assert.assertTrue
 import org.junit.Before
 import org.junit.Test
@@ -45,6 +67,7 @@
     protected val batteryController = FakeBatteryController(leakCheck)
     protected val deviceProvisionedController: DeviceProvisionedController = mock()
     protected val flags: NotifPipelineFlags = mock()
+    protected val globalSettings = FakeGlobalSettings()
     protected val headsUpManager: HeadsUpManager = mock()
     protected val keyguardNotificationVisibilityProvider: KeyguardNotificationVisibilityProvider =
         mock()
@@ -53,164 +76,363 @@
     protected val mainHandler: Handler = mock()
     protected val powerManager: PowerManager = mock()
     protected val statusBarStateController = FakeStatusBarStateController()
+    protected val systemClock = FakeSystemClock()
     protected val uiEventLogger = UiEventLoggerFake()
     protected val userTracker = FakeUserTracker()
 
     protected abstract val provider: VisualInterruptionDecisionProvider
 
+    private val neverSuppresses = object : NotificationInterruptSuppressor {}
+
+    private val alwaysSuppressesInterruptions =
+        object : NotificationInterruptSuppressor {
+            override fun suppressInterruptions(entry: NotificationEntry?) = true
+        }
+
+    private val alwaysSuppressesAwakeInterruptions =
+        object : NotificationInterruptSuppressor {
+            override fun suppressAwakeInterruptions(entry: NotificationEntry?) = true
+        }
+
+    private val alwaysSuppressesAwakeHeadsUp =
+        object : NotificationInterruptSuppressor {
+            override fun suppressAwakeHeadsUp(entry: NotificationEntry?) = true
+        }
+
     @Before
     fun setUp() {
+        globalSettings.putInt(HEADS_UP_NOTIFICATIONS_ENABLED, HEADS_UP_ON)
+
         val user = UserInfo(ActivityManager.getCurrentUser(), "Current user", /* flags = */ 0)
         userTracker.set(listOf(user), /* currentUserIndex = */ 0)
 
-        whenever(headsUpManager.isSnoozed(any())).thenReturn(false)
         whenever(keyguardNotificationVisibilityProvider.shouldHideNotification(any()))
             .thenReturn(false)
     }
 
     @Test
     fun testShouldPeek() {
-        ensureStateForPeek()
+        ensurePeekState()
+        assertShouldHeadsUp(buildPeekEntry())
+    }
 
-        assertTrue(provider.makeUnloggedHeadsUpDecision(createPeekEntry()).shouldInterrupt)
+    @Test
+    fun testShouldPeek_defaultLegacySuppressor() {
+        ensurePeekState()
+        provider.addLegacySuppressor(neverSuppresses)
+        assertShouldHeadsUp(buildPeekEntry())
+    }
+
+    @Test
+    fun testShouldNotPeek_legacySuppressInterruptions() {
+        ensurePeekState()
+        provider.addLegacySuppressor(alwaysSuppressesInterruptions)
+        assertShouldNotHeadsUp(buildPeekEntry())
+    }
+
+    @Test
+    fun testShouldNotPeek_legacySuppressAwakeInterruptions() {
+        ensurePeekState()
+        provider.addLegacySuppressor(alwaysSuppressesAwakeInterruptions)
+        assertShouldNotHeadsUp(buildPeekEntry())
+    }
+
+    @Test
+    fun testShouldNotPeek_legacySuppressAwakeHeadsUp() {
+        ensurePeekState()
+        provider.addLegacySuppressor(alwaysSuppressesAwakeHeadsUp)
+        assertShouldNotHeadsUp(buildPeekEntry())
     }
 
     @Test
     fun testShouldPulse() {
-        ensureStateForPulse()
-
-        assertTrue(provider.makeUnloggedHeadsUpDecision(createPulseEntry()).shouldInterrupt)
+        ensurePulseState()
+        assertShouldHeadsUp(buildPulseEntry())
     }
 
     @Test
-    fun testShouldFsi_awake() {
-        ensureStateForAwakeFsi()
-
-        assertTrue(provider.makeUnloggedFullScreenIntentDecision(createFsiEntry()).shouldInterrupt)
+    fun testShouldPulse_defaultLegacySuppressor() {
+        ensurePulseState()
+        provider.addLegacySuppressor(neverSuppresses)
+        assertShouldHeadsUp(buildPulseEntry())
     }
 
     @Test
-    fun testShouldFsi_dreaming() {
-        ensureStateForDreamingFsi()
-
-        assertTrue(provider.makeUnloggedFullScreenIntentDecision(createFsiEntry()).shouldInterrupt)
+    fun testShouldNotPulse_legacySuppressInterruptions() {
+        ensurePulseState()
+        provider.addLegacySuppressor(alwaysSuppressesInterruptions)
+        assertShouldNotHeadsUp(buildPulseEntry())
     }
 
     @Test
-    fun testShouldFsi_keyguard() {
-        ensureStateForKeyguardFsi()
+    fun testShouldPulse_legacySuppressAwakeInterruptions() {
+        ensurePulseState()
+        provider.addLegacySuppressor(alwaysSuppressesAwakeInterruptions)
+        assertShouldHeadsUp(buildPulseEntry())
+    }
 
-        assertTrue(provider.makeUnloggedFullScreenIntentDecision(createFsiEntry()).shouldInterrupt)
+    @Test
+    fun testShouldPulse_legacySuppressAwakeHeadsUp() {
+        ensurePulseState()
+        provider.addLegacySuppressor(alwaysSuppressesAwakeHeadsUp)
+        assertShouldHeadsUp(buildPulseEntry())
     }
 
     @Test
     fun testShouldBubble() {
-        assertTrue(provider.makeAndLogBubbleDecision(createBubbleEntry()).shouldInterrupt)
+        ensureBubbleState()
+        assertShouldBubble(buildBubbleEntry())
     }
 
-    private fun ensureStateForPeek() {
-        whenever(powerManager.isScreenOn).thenReturn(true)
-        statusBarStateController.dozing = false
-        statusBarStateController.dreaming = false
+    @Test
+    fun testShouldBubble_defaultLegacySuppressor() {
+        ensureBubbleState()
+        provider.addLegacySuppressor(neverSuppresses)
+        assertShouldBubble(buildBubbleEntry())
     }
 
-    private fun ensureStateForPulse() {
-        ambientDisplayConfiguration.fakePulseOnNotificationEnabled = true
-        batteryController.setIsAodPowerSave(false)
-        statusBarStateController.dozing = true
+    @Test
+    fun testShouldNotBubble_legacySuppressInterruptions() {
+        ensureBubbleState()
+        provider.addLegacySuppressor(alwaysSuppressesInterruptions)
+        assertShouldNotBubble(buildBubbleEntry())
     }
 
-    private fun ensureStateForAwakeFsi() {
-        whenever(powerManager.isInteractive).thenReturn(false)
-        statusBarStateController.dreaming = false
-        statusBarStateController.state = SHADE
+    @Test
+    fun testShouldNotBubble_legacySuppressAwakeInterruptions() {
+        ensureBubbleState()
+        provider.addLegacySuppressor(alwaysSuppressesAwakeInterruptions)
+        assertShouldNotBubble(buildBubbleEntry())
     }
 
-    private fun ensureStateForDreamingFsi() {
-        whenever(powerManager.isInteractive).thenReturn(true)
-        statusBarStateController.dreaming = true
-        statusBarStateController.state = SHADE
+    @Test
+    fun testShouldBubble_legacySuppressAwakeHeadsUp() {
+        ensureBubbleState()
+        provider.addLegacySuppressor(alwaysSuppressesAwakeHeadsUp)
+        assertShouldBubble(buildBubbleEntry())
     }
 
-    private fun ensureStateForKeyguardFsi() {
-        whenever(powerManager.isInteractive).thenReturn(true)
-        statusBarStateController.dreaming = false
-        statusBarStateController.state = KEYGUARD
+    @Test
+    fun testShouldFsi_notInteractive() {
+        ensureNotInteractiveFsiState()
+        assertShouldFsi(buildFsiEntry())
     }
 
-    private fun createNotif(
-        hasFsi: Boolean = false,
-        bubbleMetadata: BubbleMetadata? = null
-    ): Notification {
-        return Notification.Builder(context, TEST_CHANNEL_ID)
-            .apply {
-                setContentTitle(TEST_CONTENT_TITLE)
-                setContentText(TEST_CONTENT_TEXT)
+    @Test
+    fun testShouldFsi_dreaming() {
+        ensureDreamingFsiState()
+        assertShouldFsi(buildFsiEntry())
+    }
 
-                if (hasFsi) {
-                    setFullScreenIntent(mock(), /* highPriority = */ true)
-                }
+    @Test
+    fun testShouldFsi_keyguard() {
+        ensureKeyguardFsiState()
+        assertShouldFsi(buildFsiEntry())
+    }
 
-                if (bubbleMetadata != null) {
-                    setBubbleMetadata(bubbleMetadata)
-                }
+    private data class State(
+        var hunSnoozed: Boolean? = null,
+        var isAodPowerSave: Boolean? = null,
+        var isDozing: Boolean? = null,
+        var isDreaming: Boolean? = null,
+        var isInteractive: Boolean? = null,
+        var isScreenOn: Boolean? = null,
+        var keyguardShouldHideNotification: Boolean? = null,
+        var pulseOnNotificationsEnabled: Boolean? = null,
+        var statusBarState: Int? = null,
+    )
+
+    private fun setState(state: State): Unit =
+        state.run {
+            hunSnoozed?.let { whenever(headsUpManager.isSnoozed(TEST_PACKAGE)).thenReturn(it) }
+
+            isAodPowerSave?.let { batteryController.setIsAodPowerSave(it) }
+
+            isDozing?.let { statusBarStateController.dozing = it }
+
+            isDreaming?.let { statusBarStateController.dreaming = it }
+
+            isInteractive?.let { whenever(powerManager.isInteractive).thenReturn(it) }
+
+            isScreenOn?.let { whenever(powerManager.isScreenOn).thenReturn(it) }
+
+            keyguardShouldHideNotification?.let {
+                whenever(keyguardNotificationVisibilityProvider.shouldHideNotification(any()))
+                    .thenReturn(it)
             }
-            .setContentTitle(TEST_CONTENT_TITLE)
-            .setContentText(TEST_CONTENT_TEXT)
-            .build()
-    }
 
-    private fun createBubbleMetadata(): BubbleMetadata {
-        val pendingIntent =
-            PendingIntent.getActivity(
-                context,
-                /* requestCode = */ 0,
-                Intent().setPackage(context.packageName),
-                FLAG_MUTABLE
-            )
-
-        val icon = Icon.createWithResource(context.resources, R.drawable.android)
-
-        return BubbleMetadata.Builder(pendingIntent, icon).build()
-    }
-
-    private fun createEntry(
-        notif: Notification,
-        importance: Int = IMPORTANCE_DEFAULT,
-        canBubble: Boolean? = null
-    ): NotificationEntry {
-        return NotificationEntryBuilder()
-            .apply {
-                setPkg(TEST_PACKAGE)
-                setOpPkg(TEST_PACKAGE)
-                setTag(TEST_TAG)
-                setChannel(NotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, importance))
-                setNotification(notif)
-                setImportance(importance)
-
-                if (canBubble != null) {
-                    setCanBubble(canBubble)
-                }
+            pulseOnNotificationsEnabled?.let {
+                ambientDisplayConfiguration.fakePulseOnNotificationEnabled = it
             }
-            .build()
-    }
 
-    private fun createPeekEntry() = createEntry(notif = createNotif(), importance = IMPORTANCE_HIGH)
-
-    private fun createPulseEntry() =
-        createEntry(notif = createNotif(), importance = IMPORTANCE_HIGH).also {
-            modifyRanking(it).setVisibilityOverride(VISIBILITY_NO_OVERRIDE).build()
+            statusBarState?.let { statusBarStateController.state = it }
         }
 
-    private fun createFsiEntry() =
-        createEntry(notif = createNotif(hasFsi = true), importance = IMPORTANCE_HIGH)
+    private fun ensureState(block: State.() -> Unit) =
+        State()
+            .apply {
+                keyguardShouldHideNotification = false
+                apply(block)
+            }
+            .run(this::setState)
 
-    private fun createBubbleEntry() =
-        createEntry(
-            notif = createNotif(bubbleMetadata = createBubbleMetadata()),
-            importance = IMPORTANCE_HIGH,
-            canBubble = true
-        )
+    private fun ensurePeekState(block: State.() -> Unit = {}) = ensureState {
+        hunSnoozed = false
+        isDozing = false
+        isDreaming = false
+        isScreenOn = true
+        run(block)
+    }
+
+    private fun ensurePulseState(block: State.() -> Unit = {}) = ensureState {
+        isAodPowerSave = false
+        isDozing = true
+        pulseOnNotificationsEnabled = true
+        run(block)
+    }
+
+    private fun ensureBubbleState(block: State.() -> Unit = {}) = ensureState(block)
+
+    private fun ensureNotInteractiveFsiState(block: State.() -> Unit = {}) = ensureState {
+        isDreaming = false
+        isInteractive = false
+        statusBarState = SHADE
+        run(block)
+    }
+
+    private fun ensureDreamingFsiState(block: State.() -> Unit = {}) = ensureState {
+        isDreaming = true
+        isInteractive = true
+        statusBarState = SHADE
+        run(block)
+    }
+
+    private fun ensureKeyguardFsiState(block: State.() -> Unit = {}) = ensureState {
+        isDreaming = false
+        isInteractive = true
+        statusBarState = KEYGUARD
+        run(block)
+    }
+
+    private fun assertShouldHeadsUp(entry: NotificationEntry) =
+        provider.makeUnloggedHeadsUpDecision(entry).let {
+            assertTrue("unexpected suppressed HUN: ${it.logReason}", it.shouldInterrupt)
+        }
+
+    private fun assertShouldNotHeadsUp(entry: NotificationEntry) =
+        provider.makeUnloggedHeadsUpDecision(entry).let {
+            assertFalse("unexpected unsuppressed HUN: ${it.logReason}", it.shouldInterrupt)
+        }
+
+    private fun assertShouldBubble(entry: NotificationEntry) =
+        provider.makeAndLogBubbleDecision(entry).let {
+            assertTrue("unexpected suppressed bubble: ${it.logReason}", it.shouldInterrupt)
+        }
+
+    private fun assertShouldNotBubble(entry: NotificationEntry) =
+        provider.makeAndLogBubbleDecision(entry).let {
+            assertFalse("unexpected unsuppressed bubble: ${it.logReason}", it.shouldInterrupt)
+        }
+
+    private fun assertShouldFsi(entry: NotificationEntry) =
+        provider.makeUnloggedFullScreenIntentDecision(entry).let {
+            assertTrue("unexpected suppressed FSI: ${it.logReason}", it.shouldInterrupt)
+        }
+
+    private fun assertShouldNotFsi(entry: NotificationEntry) =
+        provider.makeUnloggedFullScreenIntentDecision(entry).let {
+            assertFalse("unexpected unsuppressed FSI: ${it.logReason}", it.shouldInterrupt)
+        }
+
+    private class EntryBuilder(val context: Context) {
+        var importance = IMPORTANCE_DEFAULT
+        var suppressedVisualEffects: Int? = null
+        var whenMs: Long? = null
+        var visibilityOverride: Int? = null
+        var hasFsi = false
+        var canBubble: Boolean? = null
+        var hasBubbleMetadata = false
+        var bubbleSuppressNotification: Boolean? = null
+
+        private fun buildBubbleMetadata() =
+            BubbleMetadata.Builder(
+                    PendingIntent.getActivity(
+                        context,
+                        /* requestCode = */ 0,
+                        Intent().setPackage(context.packageName),
+                        FLAG_MUTABLE
+                    ),
+                    Icon.createWithResource(context.resources, R.drawable.android)
+                )
+                .apply { bubbleSuppressNotification?.let { setSuppressNotification(it) } }
+                .build()
+
+        fun build() =
+            Notification.Builder(context, TEST_CHANNEL_ID)
+                .apply {
+                    setContentTitle(TEST_CONTENT_TITLE)
+                    setContentText(TEST_CONTENT_TEXT)
+
+                    if (hasFsi) {
+                        setFullScreenIntent(mock(), /* highPriority = */ true)
+                    }
+
+                    whenMs?.let { setWhen(it) }
+
+                    if (hasBubbleMetadata) {
+                        setBubbleMetadata(buildBubbleMetadata())
+                    }
+                }
+                .build()
+                .let { NotificationEntryBuilder().setNotification(it) }
+                .apply {
+                    setPkg(TEST_PACKAGE)
+                    setOpPkg(TEST_PACKAGE)
+                    setTag(TEST_TAG)
+
+                    setImportance(importance)
+                    setChannel(NotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, importance))
+
+                    canBubble?.let { setCanBubble(it) }
+                }
+                .build()!!
+                .also {
+                    modifyRanking(it)
+                        .apply {
+                            suppressedVisualEffects?.let { setSuppressedVisualEffects(it) }
+                            visibilityOverride?.let { setVisibilityOverride(it) }
+                        }
+                        .build()
+                }
+    }
+
+    private fun buildEntry(block: EntryBuilder.() -> Unit) =
+        EntryBuilder(context).also(block).build()
+
+    private fun buildPeekEntry(block: EntryBuilder.() -> Unit = {}) = buildEntry {
+        importance = IMPORTANCE_HIGH
+        run(block)
+    }
+
+    private fun buildPulseEntry(block: EntryBuilder.() -> Unit = {}) = buildEntry {
+        importance = IMPORTANCE_DEFAULT
+        visibilityOverride = VISIBILITY_NO_OVERRIDE
+        run(block)
+    }
+
+    private fun buildFsiEntry(block: EntryBuilder.() -> Unit = {}) = buildEntry {
+        importance = IMPORTANCE_HIGH
+        hasFsi = true
+        run(block)
+    }
+
+    private fun buildBubbleEntry(block: EntryBuilder.() -> Unit = {}) = buildEntry {
+        canBubble = true
+        hasBubbleMetadata = true
+        run(block)
+    }
+
+    private fun whenAgo(whenAgeMs: Long) = systemClock.currentTimeMillis() - whenAgeMs
 }
 
 private const val TEST_CONTENT_TITLE = "Test Content Title"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index 978fafe..3a9d111 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -409,15 +409,14 @@
         )
     }
 
-    private suspend fun showLockscreenWithQSExpanded() {
+    private suspend fun TestScope.showLockscreenWithQSExpanded() {
         shadeRepository.setLockscreenShadeExpansion(0f)
         shadeRepository.setQsExpansion(1f)
         keyguardRepository.setStatusBarState(StatusBarState.SHADE_LOCKED)
-        keyguardTransitionRepository.sendTransitionStep(
-            TransitionStep(
-                to = KeyguardState.LOCKSCREEN,
-                transitionState = TransitionState.FINISHED
-            )
+        keyguardTransitionRepository.sendTransitionSteps(
+            from = KeyguardState.AOD,
+            to = KeyguardState.LOCKSCREEN,
+            this,
         )
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 05fd6d2..4a20f83 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -20,6 +20,8 @@
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
 import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
+import static android.provider.Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED;
+import static android.provider.Settings.Global.HEADS_UP_ON;
 
 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
 import static com.android.systemui.statusbar.StatusBarState.SHADE;
@@ -51,7 +53,6 @@
 import android.app.WallpaperManager;
 import android.app.trust.TrustManager;
 import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
 import android.content.IntentFilter;
 import android.hardware.devicestate.DeviceStateManager;
 import android.hardware.display.AmbientDisplayConfiguration;
@@ -180,11 +181,16 @@
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.concurrency.MessageRouterImpl;
 import com.android.systemui.util.kotlin.JavaAdapter;
+import com.android.systemui.util.settings.FakeGlobalSettings;
+import com.android.systemui.util.settings.GlobalSettings;
 import com.android.systemui.util.time.FakeSystemClock;
+import com.android.systemui.util.time.SystemClock;
 import com.android.systemui.volume.VolumeComponent;
 import com.android.wm.shell.bubbles.Bubbles;
 import com.android.wm.shell.startingsurface.StartingSurface;
 
+import dagger.Lazy;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -198,8 +204,6 @@
 
 import javax.inject.Provider;
 
-import dagger.Lazy;
-
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper(setAsMainLooper = true)
@@ -319,6 +323,7 @@
 
     private ShadeController mShadeController;
     private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
+    private final FakeGlobalSettings mFakeGlobalSettings = new FakeGlobalSettings();
     private final FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock);
     private final FakeExecutor mUiBgExecutor = new FakeExecutor(mFakeSystemClock);
     private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
@@ -349,8 +354,10 @@
         mPowerManager = new PowerManager(mContext, mPowerManagerService, thermalService,
                 Handler.createAsync(Looper.myLooper()));
 
+        mFakeGlobalSettings.putInt(HEADS_UP_NOTIFICATIONS_ENABLED, HEADS_UP_ON);
+
         mNotificationInterruptStateProvider =
-                new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(),
+                new TestableNotificationInterruptStateProviderImpl(
                         mPowerManager,
                         mAmbientDisplayConfiguration,
                         mStatusBarStateController,
@@ -363,7 +370,9 @@
                         mock(KeyguardNotificationVisibilityProvider.class),
                         mock(UiEventLogger.class),
                         mUserTracker,
-                        mDeviceProvisionedController);
+                        mDeviceProvisionedController,
+                        mFakeSystemClock,
+                        mFakeGlobalSettings);
 
         mContext.addMockSystemService(TrustManager.class, mock(TrustManager.class));
         mContext.addMockSystemService(FingerprintManager.class, mock(FingerprintManager.class));
@@ -1162,7 +1171,6 @@
             NotificationInterruptStateProviderImpl {
 
         TestableNotificationInterruptStateProviderImpl(
-                ContentResolver contentResolver,
                 PowerManager powerManager,
                 AmbientDisplayConfiguration ambientDisplayConfiguration,
                 StatusBarStateController controller,
@@ -1175,9 +1183,10 @@
                 KeyguardNotificationVisibilityProvider keyguardNotificationVisibilityProvider,
                 UiEventLogger uiEventLogger,
                 UserTracker userTracker,
-                DeviceProvisionedController deviceProvisionedController) {
+                DeviceProvisionedController deviceProvisionedController,
+                SystemClock systemClock,
+                GlobalSettings globalSettings) {
             super(
-                    contentResolver,
                     powerManager,
                     ambientDisplayConfiguration,
                     batteryController,
@@ -1190,7 +1199,9 @@
                     keyguardNotificationVisibilityProvider,
                     uiEventLogger,
                     userTracker,
-                    deviceProvisionedController
+                    deviceProvisionedController,
+                    systemClock,
+                    globalSettings
             );
             mUseHeadsUp = true;
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/TraceUtilsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/tracing/TraceUtilsTest.kt
similarity index 96%
rename from packages/SystemUI/tests/src/com/android/systemui/util/TraceUtilsTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/tracing/TraceUtilsTest.kt
index 6aad0ad..8fb5ff8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/TraceUtilsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/tracing/TraceUtilsTest.kt
@@ -12,7 +12,7 @@
  * permissions and limitations under the License.
  */
 
-package com.android.systemui.util
+package com.android.systemui.tracing
 
 import android.os.Handler
 import android.os.Looper
@@ -76,7 +76,7 @@
     @Test
     fun testLongTraceSection_doesNotThrow_whenUsingHelper() {
         traceSection(SECTION_NAME_THATS_TOO_LONG) {
-            Log.v(TAG, "com.android.systemui.util.traceSection() block.")
+            Log.v(TAG, "com.android.systemui.tracing.traceSection() block.")
         }
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index ec808c7..8309b85 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -18,6 +18,8 @@
 
 import static android.app.Notification.FLAG_BUBBLE;
 import static android.app.PendingIntent.FLAG_MUTABLE;
+import static android.provider.Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED;
+import static android.provider.Settings.Global.HEADS_UP_ON;
 import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_DELETED;
 import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED;
 import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
@@ -157,6 +159,8 @@
 import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository;
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
 import com.android.systemui.user.domain.interactor.UserSwitcherInteractor;
+import com.android.systemui.util.settings.FakeGlobalSettings;
+import com.android.systemui.util.time.SystemClock;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.WindowManagerShellWrapper;
 import com.android.wm.shell.bubbles.Bubble;
@@ -476,7 +480,6 @@
                 mKeyguardStateController,
                 mScreenOffAnimationController,
                 mAuthController,
-                mShadeExpansionStateManager,
                 () -> mShadeInteractor,
                 mShadeWindowLogger,
                 () -> mSelectedUserInteractor
@@ -507,8 +510,11 @@
         when(mUserManager.getProfiles(ActivityManager.getCurrentUser())).thenReturn(
                 Collections.singletonList(mock(UserInfo.class)));
 
+        final FakeGlobalSettings fakeGlobalSettings = new FakeGlobalSettings();
+        fakeGlobalSettings.putInt(HEADS_UP_NOTIFICATIONS_ENABLED, HEADS_UP_ON);
+
         TestableNotificationInterruptStateProviderImpl interruptionStateProvider =
-                new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(),
+                new TestableNotificationInterruptStateProviderImpl(
                         mock(PowerManager.class),
                         mock(AmbientDisplayConfiguration.class),
                         mock(StatusBarStateController.class),
@@ -521,7 +527,9 @@
                         mock(KeyguardNotificationVisibilityProvider.class),
                         mock(UiEventLogger.class),
                         mock(UserTracker.class),
-                        mock(DeviceProvisionedController.class)
+                        mock(DeviceProvisionedController.class),
+                        mock(SystemClock.class),
+                        fakeGlobalSettings
                 );
 
         mShellTaskOrganizer = new ShellTaskOrganizer(mock(ShellInit.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java
index 0df235d..975555c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.wmshell;
 
-import android.content.ContentResolver;
 import android.hardware.display.AmbientDisplayConfiguration;
 import android.os.Handler;
 import android.os.PowerManager;
@@ -32,12 +31,13 @@
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.settings.GlobalSettings;
+import com.android.systemui.util.time.SystemClock;
 
 public class TestableNotificationInterruptStateProviderImpl
         extends NotificationInterruptStateProviderImpl {
 
     TestableNotificationInterruptStateProviderImpl(
-            ContentResolver contentResolver,
             PowerManager powerManager,
             AmbientDisplayConfiguration ambientDisplayConfiguration,
             StatusBarStateController statusBarStateController,
@@ -50,8 +50,10 @@
             KeyguardNotificationVisibilityProvider keyguardNotificationVisibilityProvider,
             UiEventLogger uiEventLogger,
             UserTracker userTracker,
-            DeviceProvisionedController deviceProvisionedController) {
-        super(contentResolver,
+            DeviceProvisionedController deviceProvisionedController,
+            SystemClock systemClock,
+            GlobalSettings globalSettings) {
+        super(
                 powerManager,
                 ambientDisplayConfiguration,
                 batteryController,
@@ -64,7 +66,9 @@
                 keyguardNotificationVisibilityProvider,
                 uiEventLogger,
                 userTracker,
-                deviceProvisionedController);
+                deviceProvisionedController,
+                systemClock,
+                globalSettings);
         mUseHeadsUp = true;
     }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
index 3c49c58..800593f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
@@ -56,6 +56,14 @@
     @Deprecated("Use ShadeInteractor instead")
     override val legacyExpandedOrAwaitingInputTransfer = _legacyExpandedOrAwaitingInputTransfer
 
+    private val _legacyIsQsExpanded = MutableStateFlow(false)
+    @Deprecated("Use ShadeInteractor instead") override val legacyIsQsExpanded = _legacyIsQsExpanded
+
+    @Deprecated("Use ShadeInteractor instead")
+    override fun setLegacyIsQsExpanded(legacyIsQsExpanded: Boolean) {
+        _legacyIsQsExpanded.value = legacyIsQsExpanded
+    }
+
     @Deprecated("Use ShadeInteractor instead")
     override fun setLegacyExpandedOrAwaitingInputTransfer(
         legacyExpandedOrAwaitingInputTransfer: Boolean
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/FakeStatusBarStateController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/FakeStatusBarStateController.kt
index 19fdb6d..a65813a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/FakeStatusBarStateController.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/FakeStatusBarStateController.kt
@@ -104,7 +104,7 @@
 
     override fun isDreaming() = dreaming
 
-    override fun setIsDreaming(drreaming: Boolean): Boolean {
+    override fun setIsDreaming(dreaming: Boolean): Boolean {
         dreaming != this.dreaming || return false
         this.dreaming = dreaming
         callbacks.forEach { it.onDreamingChanged(dreaming) }
diff --git a/packages/SystemUI/tools/lint/baseline.xml b/packages/SystemUI/tools/lint/baseline.xml
index 301c9b8..43f8300 100644
--- a/packages/SystemUI/tools/lint/baseline.xml
+++ b/packages/SystemUI/tools/lint/baseline.xml
@@ -1271,17 +1271,6 @@
     </issue>
 
     <issue
-        id="MergeRootFrame"
-        message="This `&lt;FrameLayout>` can be replaced with a `&lt;merge>` tag"
-        errorLine1="&lt;FrameLayout"
-        errorLine2="^">
-        <location
-            file="res/layout/volume_dnd_icon.xml"
-            line="16"
-            column="1"/>
-    </issue>
-
-    <issue
         id="InefficientWeight"
         message="Use a `layout_height` of `0dp` instead of `wrap_content` for better performance"
         errorLine1="                android:layout_height=&quot;wrap_content&quot;"
@@ -88853,17 +88842,6 @@
         errorLine1="    &lt;ImageView"
         errorLine2="    ^">
         <location
-            file="res/layout/volume_dnd_icon.xml"
-            line="23"
-            column="5"/>
-    </issue>
-
-    <issue
-        id="ContentDescription"
-        message="[Accessibility] Missing `contentDescription` attribute on image"
-        errorLine1="    &lt;ImageView"
-        errorLine2="    ^">
-        <location
             file="res/layout/wireless_charging_layout.xml"
             line="26"
             column="5"/>
@@ -89783,15 +89761,4 @@
             column="22"/>
     </issue>
 
-    <issue
-        id="RtlHardcoded"
-        message="Use &quot;`end`&quot; instead of &quot;`right`&quot; to ensure correct behavior in right-to-left locales"
-        errorLine1="        android:layout_gravity=&quot;right|top&quot;"
-        errorLine2="                                ~~~~~~~~~">
-        <location
-            file="res/layout/volume_dnd_icon.xml"
-            line="26"
-            column="33"/>
-    </issue>
-
 </issues>
diff --git a/services/Android.bp b/services/Android.bp
index aca8409..f1534b4 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -41,7 +41,10 @@
     name: "system_optimized_java_defaults",
     module_type: "java_defaults",
     config_namespace: "ANDROID",
-    bool_variables: ["SYSTEM_OPTIMIZE_JAVA"],
+    bool_variables: [
+        "SYSTEM_OPTIMIZE_JAVA",
+        "FULL_SYSTEM_OPTIMIZE_JAVA",
+    ],
     properties: [
         "optimize",
         "dxflags",
@@ -56,6 +59,7 @@
                 enabled: true,
                 // TODO(b/210510433): Enable optimizations after improving
                 // retracing infra.
+                // See also FULL_SYSTEM_OPTIMIZE_JAVA.
                 optimize: false,
                 shrink: true,
                 ignore_warnings: false,
@@ -81,6 +85,12 @@
                 dxflags: ["--debug"],
             },
         },
+        // Allow form factors to opt-in full system java optimization
+        FULL_SYSTEM_OPTIMIZE_JAVA: {
+            optimize: {
+                optimize: true,
+            },
+        },
     },
 }
 
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index c791498..2c60893 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -756,8 +756,7 @@
         public PendingIntent requestNotificationAccess(ComponentName component, int userId)
                 throws RemoteException {
             String callingPackage = component.getPackageName();
-            checkCanCallNotificationApi(callingPackage);
-            // TODO: check userId.
+            checkCanCallNotificationApi(callingPackage, userId);
             if (component.flattenToString().length() > MAX_CN_LENGTH) {
                 throw new IllegalArgumentException("Component name is too long.");
             }
@@ -783,7 +782,7 @@
         @Deprecated
         @Override
         public boolean hasNotificationAccess(ComponentName component) throws RemoteException {
-            checkCanCallNotificationApi(component.getPackageName());
+            checkCanCallNotificationApi(component.getPackageName(), getCallingUserId());
             NotificationManager nm = getContext().getSystemService(NotificationManager.class);
             return nm.isNotificationListenerAccessGranted(component);
         }
@@ -1017,8 +1016,7 @@
             createNewAssociation(userId, packageName, macAddressObj, null, null, false);
         }
 
-        private void checkCanCallNotificationApi(String callingPackage) {
-            final int userId = getCallingUserId();
+        private void checkCanCallNotificationApi(String callingPackage, int userId) {
             enforceCallerIsSystemOr(userId, callingPackage);
 
             if (getCallingUid() == SYSTEM_UID) return;
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index 959f69e..92af68b 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -361,14 +361,6 @@
                 @NonNull IVirtualDeviceSoundEffectListener soundEffectListener) {
             createVirtualDevice_enforcePermission();
             attributionSource.enforceCallingUid();
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                if (Flags.moreLogs()) {
-                    Slog.i(TAG, "Creating VirtualDevice");
-                }
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
 
             final int callingUid = getCallingUid();
             final String packageName = attributionSource.getPackageName();
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 15fc2dc..0223509 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -61,6 +61,7 @@
 import android.app.PendingIntent;
 import android.app.admin.SecurityLog;
 import android.app.usage.StorageStatsManager;
+import android.content.AttributionSource;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -2138,8 +2139,13 @@
                         | MATCH_DIRECT_BOOT_UNAWARE | MATCH_UNINSTALLED_PACKAGES | MATCH_ANY_USER,
                         userId, Process.myUid())) {
             try {
-                boolean hasLegacy = mIAppOpsService.checkOperation(OP_LEGACY_STORAGE, ai.uid,
-                        ai.packageName) == MODE_ALLOWED;
+                final AttributionSource attributionSource = new AttributionSource.Builder(ai.uid)
+                        .setPackageName(ai.packageName)
+                        .build();
+                boolean hasLegacy =
+                        mIAppOpsService.checkOperationWithState(
+                                        OP_LEGACY_STORAGE, attributionSource.asState())
+                                == MODE_ALLOWED;
                 updateLegacyStorageApps(ai.packageName, ai.uid, hasLegacy);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Failed to check legacy op for package " + ai.packageName, e);
@@ -4540,8 +4546,11 @@
             // sharing the uid and allow same level of storage access for all packages even if
             // one of the packages has the appop granted.
             for (String uidPackageName : packagesForUid) {
-                if (mIAppOpsService.checkOperation(
-                        OP_REQUEST_INSTALL_PACKAGES, uid, uidPackageName) == MODE_ALLOWED) {
+                final AttributionSource attributionSource =
+                        new AttributionSource.Builder(uid).setPackageName(uidPackageName).build();
+                if (mIAppOpsService.checkOperationWithState(
+                                OP_REQUEST_INSTALL_PACKAGES, attributionSource.asState())
+                        == MODE_ALLOWED) {
                     hasInstallOp = true;
                     break;
                 }
@@ -4838,8 +4847,11 @@
         @Override
         public boolean hasExternalStorageAccess(int uid, String packageName) {
             try {
-                final int opMode = mIAppOpsService.checkOperation(
-                        OP_MANAGE_EXTERNAL_STORAGE, uid, packageName);
+                final AttributionSource attributionSource =
+                        new AttributionSource.Builder(uid).setPackageName(packageName).build();
+                final int opMode =
+                        mIAppOpsService.checkOperationWithState(
+                                OP_MANAGE_EXTERNAL_STORAGE, attributionSource.asState());
                 if (opMode == AppOpsManager.MODE_DEFAULT) {
                     return mIPackageManager.checkUidPermission(
                             MANAGE_EXTERNAL_STORAGE, uid) == PERMISSION_GRANTED;
diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING
index cd0a9d2..afc0dd1 100644
--- a/services/core/java/com/android/server/TEST_MAPPING
+++ b/services/core/java/com/android/server/TEST_MAPPING
@@ -116,5 +116,17 @@
             ],
             "file_patterns": ["VpnManagerService\\.java"]
         }
+    ],
+    "postsubmit": [
+        // these tests need to establish SLO baseline, see go/test-mapping-slo-guide
+        {
+            "name": "CtsPackageManagerTestCases"
+        },
+        {
+            "name": "CtsSuspendAppsTestCases"
+        },
+        {
+            "name": "FrameworksNetTests"
+        }
     ]
 }
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 5f1a7e7..4bdb4da 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -167,6 +167,7 @@
 import android.compat.annotation.EnabledAfter;
 import android.compat.annotation.EnabledSince;
 import android.compat.annotation.Overridable;
+import android.content.AttributionSource;
 import android.content.ComponentName;
 import android.content.ComponentName.WithComponentName;
 import android.content.Context;
@@ -1100,8 +1101,12 @@
                             SystemClock.uptimeMillis()); // Use current time, not lastActivity.
                 }
             }
-            mAm.mAppOpsService.startOperation(AppOpsManager.getToken(mAm.mAppOpsService),
-                    AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, null,
+            final AttributionSource attributionSource = new AttributionSource.Builder(r.appInfo.uid)
+                    .setPackageName(r.packageName)
+                    .build();
+            mAm.mAppOpsService.startOperationWithState(AppOpsManager.getToken(mAm.mAppOpsService),
+                    AppOpsManager.OP_START_FOREGROUND,
+                    attributionSource.asState(),
                     true, false, null, false, AppOpsManager.ATTRIBUTION_FLAGS_NONE,
                     AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE);
         }
@@ -2451,10 +2456,15 @@
                             stopProcStatsOp = false;
                         }
 
-                        mAm.mAppOpsService.startOperation(
+                        final AttributionSource attributionSource = new AttributionSource
+                                .Builder(r.appInfo.uid)
+                                .setPackageName(r.packageName)
+                                .build();
+                        mAm.mAppOpsService.startOperationWithState(
                                 AppOpsManager.getToken(mAm.mAppOpsService),
-                                AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName,
-                                null, true, false, "", false, AppOpsManager.ATTRIBUTION_FLAGS_NONE,
+                                AppOpsManager.OP_START_FOREGROUND, attributionSource.asState(),
+                                true, false, "", false,
+                                AppOpsManager.ATTRIBUTION_FLAGS_NONE,
                                 AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE);
                         registerAppOpCallbackLocked(r);
                         mAm.updateForegroundServiceUsageStats(r.name, r.userId, true);
@@ -2514,10 +2524,13 @@
                 if (alreadyStartedOp) {
                     // If we had previously done a start op for direct foreground start,
                     // we have cleared the flag so can now drop it.
-                    mAm.mAppOpsService.finishOperation(
+                    final AttributionSource attributionSource = new AttributionSource
+                            .Builder(r.appInfo.uid)
+                            .setPackageName(r.packageName)
+                            .build();
+                    mAm.mAppOpsService.finishOperationWithState(
                             AppOpsManager.getToken(mAm.mAppOpsService),
-                            AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName,
-                            null);
+                            AppOpsManager.OP_START_FOREGROUND, attributionSource.asState());
                 }
             }
         } else {
@@ -2560,9 +2573,13 @@
                                 SystemClock.uptimeMillis());
                     }
                 }
-                mAm.mAppOpsService.finishOperation(
+                final AttributionSource attributionSource =
+                        new AttributionSource.Builder(r.appInfo.uid)
+                                .setPackageName(r.packageName)
+                                .build();
+                mAm.mAppOpsService.finishOperationWithState(
                         AppOpsManager.getToken(mAm.mAppOpsService),
-                        AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, null);
+                        AppOpsManager.OP_START_FOREGROUND, attributionSource.asState());
                 unregisterAppOpCallbackLocked(r);
                 logFGSStateChangeLocked(r,
                         FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT,
@@ -5704,8 +5721,12 @@
                             SystemClock.uptimeMillis());
                 }
             }
-            mAm.mAppOpsService.finishOperation(AppOpsManager.getToken(mAm.mAppOpsService),
-                    AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, null);
+            final AttributionSource attributionSource = new AttributionSource
+                    .Builder(r.appInfo.uid)
+                    .setPackageName(r.packageName)
+                    .build();
+            mAm.mAppOpsService.finishOperationWithState(AppOpsManager.getToken(mAm.mAppOpsService),
+                    AppOpsManager.OP_START_FOREGROUND, attributionSource.asState());
             mServiceFGAnrTimer.cancel(r);
             if (r.app != null) {
                 Message msg = mAm.mHandler.obtainMessage(
@@ -5770,9 +5791,13 @@
                             SystemClock.uptimeMillis());
                 }
             }
-            mAm.mAppOpsService.finishOperation(
+            final AttributionSource attributionSource = new AttributionSource
+                    .Builder(r.appInfo.uid)
+                    .setPackageName(r.packageName)
+                    .build();
+            mAm.mAppOpsService.finishOperationWithState(
                     AppOpsManager.getToken(mAm.mAppOpsService),
-                    AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, null);
+                    AppOpsManager.OP_START_FOREGROUND, attributionSource.asState());
             unregisterAppOpCallbackLocked(r);
             r.mFgsExitTime = SystemClock.uptimeMillis();
             logFGSStateChangeLocked(r,
@@ -8491,8 +8516,11 @@
 
         mAm.mBatteryStatsService.noteServiceStartRunning(callingUid, callingPackage,
                 cn.getClassName());
-        mAm.mAppOpsService.startOperation(AppOpsManager.getToken(mAm.mAppOpsService),
-                AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, null,
+        final AttributionSource attributionSource = new AttributionSource.Builder(r.appInfo.uid)
+                .setPackageName(r.packageName)
+                .build();
+        mAm.mAppOpsService.startOperationWithState(AppOpsManager.getToken(mAm.mAppOpsService),
+                AppOpsManager.OP_START_FOREGROUND, attributionSource.asState(),
                 true, false, null, false,
                 AppOpsManager.ATTRIBUTION_FLAGS_NONE, AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE);
         registerAppOpCallbackLocked(r);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ae79f19..88bb66f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -428,10 +428,11 @@
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.MemInfoReader;
 import com.android.internal.util.Preconditions;
-import com.android.internal.util.function.HeptFunction;
 import com.android.internal.util.function.HexFunction;
+import com.android.internal.util.function.NonaFunction;
 import com.android.internal.util.function.QuadFunction;
 import com.android.internal.util.function.QuintFunction;
+import com.android.internal.util.function.TriFunction;
 import com.android.internal.util.function.UndecFunction;
 import com.android.server.AlarmManagerInternal;
 import com.android.server.BootReceiver;
@@ -3150,8 +3151,11 @@
     }
 
     private boolean hasUsageStatsPermission(String callingPackage, int callingUid, int callingPid) {
-        final int mode = mAppOpsService.noteOperation(AppOpsManager.OP_GET_USAGE_STATS,
-                callingUid, callingPackage, null, false, "", false).getOpMode();
+        final AttributionSource attributionSource = new AttributionSource.Builder(callingUid)
+                .setPackageName(callingPackage)
+                .build();
+        final int mode = mAppOpsService.noteOperationWithState(AppOpsManager.OP_GET_USAGE_STATS,
+                attributionSource.asState(), false, "", false).getOpMode();
         if (mode == AppOpsManager.MODE_DEFAULT) {
             return checkPermission(Manifest.permission.PACKAGE_USAGE_STATS, callingPid, callingUid)
                     == PackageManager.PERMISSION_GRANTED;
@@ -5929,9 +5933,18 @@
         @Override
         public int noteOp(String op, int uid, String packageName) {
             // TODO moltmann: Allow to specify featureId
-            return mActivityManagerService.mAppOpsService
-                    .noteOperation(AppOpsManager.strOpToOp(op), uid, packageName, null,
-                            false, "", false).getOpMode();
+            final AttributionSource attributionSource = new AttributionSource.Builder(uid)
+                    .setPackageName(packageName)
+                    .build();
+            return mActivityManagerService
+                    .mAppOpsService
+                    .noteOperationWithState(
+                            AppOpsManager.strOpToOp(op),
+                            attributionSource.asState(),
+                            false,
+                            "",
+                            false)
+                    .getOpMode();
         }
 
         @Override
@@ -15934,7 +15947,7 @@
         try {
             sdkSandboxInfo =
                     sandboxManagerLocal.getSdkSandboxApplicationInfoForInstrumentation(
-                            sdkSandboxClientAppInfo, userId, isSdkInSandbox);
+                            sdkSandboxClientAppInfo, isSdkInSandbox);
         } catch (NameNotFoundException e) {
             reportStartInstrumentationFailureLocked(
                     watcher, className, "Can't find SdkSandbox package");
@@ -20059,20 +20072,26 @@
         }
 
         @Override
-        public int checkOperation(int code, int uid, String packageName,
-                String attributionTag, boolean raw,
-                QuintFunction<Integer, Integer, String, String, Boolean, Integer> superImpl) {
+        public int checkOperation(int code, AttributionSource attributionSource, boolean raw,
+                TriFunction<Integer, AttributionSource, Boolean, Integer> superImpl) {
+            final int uid = attributionSource.getUid();
+
             if (uid == mTargetUid && isTargetOp(code)) {
                 final int shellUid = UserHandle.getUid(UserHandle.getUserId(uid),
                         Process.SHELL_UID);
+                final AttributionSource shellAttributionSource =
+                        new AttributionSource.Builder(shellUid)
+                                .setPackageName("com.android.shell")
+                                .build();
+
                 final long identity = Binder.clearCallingIdentity();
                 try {
-                    return superImpl.apply(code, shellUid, "com.android.shell", null, raw);
+                    return superImpl.apply(code, shellAttributionSource, raw);
                 } finally {
                     Binder.restoreCallingIdentity(identity);
                 }
             }
-            return superImpl.apply(code, uid, packageName, attributionTag, raw);
+            return superImpl.apply(code, attributionSource, raw);
         }
 
         @Override
@@ -20092,23 +20111,30 @@
         }
 
         @Override
-        public SyncNotedAppOp noteOperation(int code, int uid, @Nullable String packageName,
-                @Nullable String featureId, boolean shouldCollectAsyncNotedOp,
+        public SyncNotedAppOp noteOperation(int code, AttributionSource attributionSource,
+                boolean shouldCollectAsyncNotedOp,
                 @Nullable String message, boolean shouldCollectMessage,
-                @NonNull HeptFunction<Integer, Integer, String, String, Boolean, String, Boolean,
+                @NonNull QuintFunction<Integer, AttributionSource, Boolean, String, Boolean,
                         SyncNotedAppOp> superImpl) {
+            final int uid = attributionSource.getUid();
+            final String attributionTag = attributionSource.getAttributionTag();
             if (uid == mTargetUid && isTargetOp(code)) {
                 final int shellUid = UserHandle.getUid(UserHandle.getUserId(uid),
                         Process.SHELL_UID);
                 final long identity = Binder.clearCallingIdentity();
+                final AttributionSource shellAttributionSource =
+                        new AttributionSource.Builder(shellUid)
+                                .setPackageName("com.android.shell")
+                                .setAttributionTag(attributionTag)
+                                .build();
                 try {
-                    return superImpl.apply(code, shellUid, "com.android.shell", featureId,
+                    return superImpl.apply(code, shellAttributionSource,
                             shouldCollectAsyncNotedOp, message, shouldCollectMessage);
                 } finally {
                     Binder.restoreCallingIdentity(identity);
                 }
             }
-            return superImpl.apply(code, uid, packageName, featureId, shouldCollectAsyncNotedOp,
+            return superImpl.apply(code, attributionSource, shouldCollectAsyncNotedOp,
                     message, shouldCollectMessage);
         }
 
@@ -20139,28 +20165,37 @@
         }
 
         @Override
-        public SyncNotedAppOp startOperation(IBinder token, int code, int uid,
-                @Nullable String packageName, @Nullable String attributionTag,
+        public SyncNotedAppOp startOperation(IBinder token, int code,
+                AttributionSource attributionSource,
                 boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp,
                 @Nullable String message, boolean shouldCollectMessage,
                 @AttributionFlags int attributionFlags, int attributionChainId,
-                @NonNull UndecFunction<IBinder, Integer, Integer, String, String, Boolean,
+                @NonNull NonaFunction<IBinder, Integer, AttributionSource, Boolean,
                         Boolean, String, Boolean, Integer, Integer, SyncNotedAppOp> superImpl) {
+            final int uid = attributionSource.getUid();
+            final String attributionTag = attributionSource.getAttributionTag();
+
             if (uid == mTargetUid && isTargetOp(code)) {
                 final int shellUid = UserHandle.getUid(UserHandle.getUserId(uid),
                         Process.SHELL_UID);
                 final long identity = Binder.clearCallingIdentity();
                 try {
-                    return superImpl.apply(token, code, shellUid, "com.android.shell",
-                            attributionTag, startIfModeDefault, shouldCollectAsyncNotedOp, message,
+                    final AttributionSource shellAttributionSource =
+                            new AttributionSource.Builder(shellUid)
+                                    .setPackageName("com.android.shell")
+                                    .setAttributionTag(attributionTag)
+                                    .build();
+
+                    return superImpl.apply(token, code, shellAttributionSource,
+                            startIfModeDefault, shouldCollectAsyncNotedOp, message,
                             shouldCollectMessage, attributionFlags, attributionChainId);
                 } finally {
                     Binder.restoreCallingIdentity(identity);
                 }
             }
-            return superImpl.apply(token, code, uid, packageName, attributionTag,
-                    startIfModeDefault, shouldCollectAsyncNotedOp, message, shouldCollectMessage,
-                    attributionFlags, attributionChainId);
+            return superImpl.apply(token, code, attributionSource, startIfModeDefault,
+                    shouldCollectAsyncNotedOp, message, shouldCollectMessage, attributionFlags,
+                    attributionChainId);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 69bf612..26b23ff 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -1179,7 +1179,7 @@
                 synchronized (mInternal.mOomAdjuster.mCachedAppOptimizer.mFreezerLock) {
                     app.mOptRecord.setFreezeSticky(isSticky);
                     mInternal.mOomAdjuster.mCachedAppOptimizer.unfreezeAppInternalLSP(app, 0,
-                            false);
+                            true);
                 }
             }
         }
diff --git a/services/core/java/com/android/server/am/AppPermissionTracker.java b/services/core/java/com/android/server/am/AppPermissionTracker.java
index 18a9153..947fcd3 100644
--- a/services/core/java/com/android/server/am/AppPermissionTracker.java
+++ b/services/core/java/com/android/server/am/AppPermissionTracker.java
@@ -37,6 +37,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.AppOpsManager;
+import android.content.AttributionSource;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager.OnPermissionsChangedListener;
@@ -192,7 +193,11 @@
         if (DEBUG_PERMISSION_TRACKER) {
             final IAppOpsService appOpsService = mInjector.getIAppOpsService();
             try {
-                final int mode = appOpsService.checkOperation(op, uid, packageName);
+                final AttributionSource attributionSource = new AttributionSource.Builder(uid)
+                        .setPackageName(packageName)
+                        .build();
+                final int mode =
+                        appOpsService.checkOperationWithState(op, attributionSource.asState());
                 Slog.i(TAG, "onOpChanged: " + opToPublicName(op)
                         + " " + UserHandle.formatUid(uid)
                         + " " + packageName + " " + mode);
@@ -307,7 +312,11 @@
                 final IAppOpsService appOpsService = mInjector.getIAppOpsService();
                 for (String pkg : packages) {
                     try {
-                        final int mode = appOpsService.checkOperation(mAppOp, mUid, pkg);
+                        final AttributionSource attributionSource =
+                                new AttributionSource.Builder(mUid).setPackageName(pkg).build();
+                        final int mode =
+                                appOpsService.checkOperationWithState(
+                                        mAppOp, attributionSource.asState());
                         if (mode == AppOpsManager.MODE_ALLOWED) {
                             mAppOpAllowed = true;
                             return;
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 052b0c2..d6997da 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -1143,22 +1143,32 @@
             }
         }, RARELY_USED_PACKAGES_INITIALIZATION_DELAY_MILLIS);
 
-        getPackageManagerInternal().setExternalSourcesPolicy(
-                new PackageManagerInternal.ExternalSourcesPolicy() {
-                    @Override
-                    public int getPackageTrustedToInstallApps(String packageName, int uid) {
-                        int appOpMode = checkOperation(AppOpsManager.OP_REQUEST_INSTALL_PACKAGES,
-                                uid, packageName);
-                        switch (appOpMode) {
-                            case AppOpsManager.MODE_ALLOWED:
-                                return PackageManagerInternal.ExternalSourcesPolicy.USER_TRUSTED;
-                            case AppOpsManager.MODE_ERRORED:
-                                return PackageManagerInternal.ExternalSourcesPolicy.USER_BLOCKED;
-                            default:
-                                return PackageManagerInternal.ExternalSourcesPolicy.USER_DEFAULT;
-                        }
-                    }
-                });
+        getPackageManagerInternal()
+                .setExternalSourcesPolicy(
+                        new PackageManagerInternal.ExternalSourcesPolicy() {
+                            @Override
+                            public int getPackageTrustedToInstallApps(String packageName, int uid) {
+                                final AttributionSource attributionSource =
+                                        new AttributionSource.Builder(uid)
+                                                .setPackageName(packageName)
+                                                .build();
+                                int appOpMode =
+                                        checkOperationWithState(
+                                                AppOpsManager.OP_REQUEST_INSTALL_PACKAGES,
+                                                attributionSource.asState());
+                                switch (appOpMode) {
+                                    case AppOpsManager.MODE_ALLOWED:
+                                        return PackageManagerInternal.ExternalSourcesPolicy
+                                                .USER_TRUSTED;
+                                    case AppOpsManager.MODE_ERRORED:
+                                        return PackageManagerInternal.ExternalSourcesPolicy
+                                                .USER_BLOCKED;
+                                    default:
+                                        return PackageManagerInternal.ExternalSourcesPolicy
+                                                .USER_DEFAULT;
+                                }
+                            }
+                        });
     }
 
     @VisibleForTesting
@@ -2534,22 +2544,41 @@
         }
     }
 
+    /** @deprecated Use {@link #checkOperationWithStateRaw} instead. */
     @Override
     public int checkOperationRaw(int code, int uid, String packageName,
-            @Nullable String attributionTag) {
-        return mCheckOpsDelegateDispatcher.checkOperation(code, uid, packageName, attributionTag,
-                true /*raw*/);
+             @Nullable String attributionTag) {
+        final AttributionSource attributionSource = new AttributionSource.Builder(uid)
+                .setPackageName(packageName).setAttributionTag(attributionTag).build();
+        return mCheckOpsDelegateDispatcher.checkOperation(code, attributionSource, true /*raw*/);
     }
 
     @Override
-    public int checkOperation(int code, int uid, String packageName) {
-        return mCheckOpsDelegateDispatcher.checkOperation(code, uid, packageName, null,
-                false /*raw*/);
+    public int checkOperationWithStateRaw(int code, AttributionSourceState attributionSourceState) {
+        final AttributionSource attributionSource = new AttributionSource(attributionSourceState);
+        return mCheckOpsDelegateDispatcher.checkOperation(code, attributionSource, true /*raw*/);
     }
 
-    private int checkOperationImpl(int code, int uid, String packageName,
-            @Nullable String attributionTag, boolean raw) {
+    /** @deprecated Use {@link #checkOperationWithState} instead. */
+    @Override
+    public int checkOperation(int code, int uid, String packageName) {
+        final AttributionSource attributionSource = new AttributionSource.Builder(uid)
+                .setPackageName(packageName)
+                .build();
+        return mCheckOpsDelegateDispatcher.checkOperation(code, attributionSource, false /*raw*/);
+    }
+
+    @Override
+    public int checkOperationWithState(int code, AttributionSourceState attributionSourceState) {
+        final AttributionSource attributionSource = new AttributionSource(attributionSourceState);
+        return mCheckOpsDelegateDispatcher.checkOperation(code, attributionSource, false /*raw*/);
+    }
+
+    private int checkOperationImpl(int code, AttributionSource attributionSource, boolean raw) {
         verifyIncomingOp(code);
+        final String packageName = attributionSource.getPackageName();
+        final int uid = attributionSource.getUid();
+        final String attributionTag = attributionSource.getAttributionTag();
         if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) {
             return AppOpsManager.opToDefaultMode(code);
         }
@@ -2614,7 +2643,10 @@
         if (mode != AppOpsManager.MODE_ALLOWED) {
             return mode;
         }
-        return checkOperation(code, uid, packageName);
+        final AttributionSource attributionSource = new AttributionSource.Builder(uid)
+                .setPackageName(packageName)
+                .build();
+        return checkOperationWithState(code, attributionSource.asState());
     }
 
     @Override
@@ -2758,17 +2790,38 @@
                 proxiedFlags, shouldCollectAsyncNotedOp, message, shouldCollectMessage);
     }
 
+    /** @deprecated Use {@link #noteOperationWithState} instead. */
     @Override
     public SyncNotedAppOp noteOperation(int code, int uid, String packageName,
             String attributionTag, boolean shouldCollectAsyncNotedOp, String message,
             boolean shouldCollectMessage) {
-        return mCheckOpsDelegateDispatcher.noteOperation(code, uid, packageName,
-                attributionTag, shouldCollectAsyncNotedOp, message, shouldCollectMessage);
+        final AttributionSource attributionSource = new AttributionSource.Builder(uid)
+                .setPackageName(packageName)
+                .setAttributionTag(attributionTag)
+                .build();
+        return mCheckOpsDelegateDispatcher.noteOperation(code, attributionSource,
+                shouldCollectAsyncNotedOp, message, shouldCollectMessage);
     }
 
-    private SyncNotedAppOp noteOperationImpl(int code, int uid, @Nullable String packageName,
-            @Nullable String attributionTag, boolean shouldCollectAsyncNotedOp,
+    @Override
+    public SyncNotedAppOp noteOperationWithState(
+            int code,
+            AttributionSourceState attributionSourceState,
+            boolean shouldCollectAsyncNotedOp,
+            String message,
+            boolean shouldCollectMessage) {
+        final AttributionSource attributionSource = new AttributionSource(attributionSourceState);
+        return mCheckOpsDelegateDispatcher.noteOperation(
+                code, attributionSource, shouldCollectAsyncNotedOp, message, shouldCollectMessage);
+    }
+
+    private SyncNotedAppOp noteOperationImpl(int code, AttributionSource attributionSource,
+            boolean shouldCollectAsyncNotedOp,
             @Nullable String message, boolean shouldCollectMessage) {
+        final int uid = attributionSource.getUid();
+        final String packageName = attributionSource.getPackageName();
+        final String attributionTag = attributionSource.getAttributionTag();
+
         verifyIncomingUid(uid);
         verifyIncomingOp(code);
         if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) {
@@ -3163,22 +3216,42 @@
         }
     }
 
+    /** @deprecated Use {@link #startOperationWithState} instead. */
     @Override
     public SyncNotedAppOp startOperation(IBinder token, int code, int uid,
-            @Nullable String packageName, @Nullable String attributionTag,
+             @Nullable String packageName, @Nullable String attributionTag,
+             boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp,
+             String message, boolean shouldCollectMessage, @AttributionFlags int attributionFlags,
+             int attributionChainId) {
+        final AttributionSource attributionSource = new AttributionSource.Builder(uid)
+                .setPackageName(packageName)
+                .setAttributionTag(attributionTag)
+                .build();
+        return mCheckOpsDelegateDispatcher.startOperation(token, code, attributionSource,
+                startIfModeDefault, shouldCollectAsyncNotedOp, message, shouldCollectMessage,
+                attributionFlags, attributionChainId);
+    }
+
+    @Override
+    public SyncNotedAppOp startOperationWithState(IBinder token, int code,
+            AttributionSourceState attributionSourceState,
             boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp,
             String message, boolean shouldCollectMessage, @AttributionFlags int attributionFlags,
             int attributionChainId) {
-        return mCheckOpsDelegateDispatcher.startOperation(token, code, uid, packageName,
-                attributionTag, startIfModeDefault, shouldCollectAsyncNotedOp, message,
+        final AttributionSource attributionSource = new AttributionSource(attributionSourceState);
+        return mCheckOpsDelegateDispatcher.startOperation(token, code, attributionSource,
+                startIfModeDefault, shouldCollectAsyncNotedOp, message,
                 shouldCollectMessage, attributionFlags, attributionChainId);
     }
 
-    private SyncNotedAppOp startOperationImpl(@NonNull IBinder clientId, int code, int uid,
-            @Nullable String packageName, @Nullable String attributionTag,
-            boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, @NonNull String message,
+    private SyncNotedAppOp startOperationImpl(@NonNull IBinder clientId, int code,
+            AttributionSource attributionSource, boolean startIfModeDefault,
+            boolean shouldCollectAsyncNotedOp, @NonNull String message,
             boolean shouldCollectMessage, @AttributionFlags int attributionFlags,
             int attributionChainId) {
+        final String packageName = attributionSource.getPackageName();
+        final int uid = attributionSource.getUid();
+        final String attributionTag = attributionSource.getAttributionTag();
         verifyIncomingUid(uid);
         verifyIncomingOp(code);
         if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) {
@@ -3200,7 +3273,7 @@
         int result = MODE_DEFAULT;
         if (code == OP_RECORD_AUDIO_HOTWORD || code == OP_RECEIVE_AMBIENT_TRIGGER_AUDIO
                 || code == OP_RECORD_AUDIO_SANDBOXED) {
-            result = checkOperation(OP_RECORD_AUDIO, uid, packageName);
+            result = checkOperationWithState(OP_RECORD_AUDIO, attributionSource.asState());
             // Check result
             if (result != AppOpsManager.MODE_ALLOWED) {
                 return new SyncNotedAppOp(result, code, attributionTag, packageName);
@@ -3208,7 +3281,7 @@
         }
         // As a special case for OP_CAMERA_SANDBOXED.
         if (code == OP_CAMERA_SANDBOXED) {
-            result = checkOperation(OP_CAMERA, uid, packageName);
+            result = checkOperationWithState(OP_CAMERA, attributionSource.asState());
             // Check result
             if (result != AppOpsManager.MODE_ALLOWED) {
                 return new SyncNotedAppOp(result, code, attributionTag, packageName);
@@ -3512,15 +3585,29 @@
                 packageName);
     }
 
+    /** @deprecated Use {@link #finishOperationWithState} instead. */
     @Override
     public void finishOperation(IBinder clientId, int code, int uid, String packageName,
             String attributionTag) {
-        mCheckOpsDelegateDispatcher.finishOperation(clientId, code, uid, packageName,
-                attributionTag);
+        final AttributionSource attributionSource = new AttributionSource.Builder(uid)
+                .setPackageName(packageName)
+                .setAttributionTag(attributionTag)
+                .build();
+        mCheckOpsDelegateDispatcher.finishOperation(clientId, code, attributionSource);
     }
 
-    private void finishOperationImpl(IBinder clientId, int code, int uid, String packageName,
-            String attributionTag) {
+    @Override
+    public void finishOperationWithState(IBinder clientId, int code,
+            AttributionSourceState attributionSourceState) {
+        final AttributionSource attributionSource = new AttributionSource(attributionSourceState);
+        mCheckOpsDelegateDispatcher.finishOperation(clientId, code, attributionSource);
+    }
+
+    private void finishOperationImpl(IBinder clientId, int code,
+            AttributionSource attributionSource) {
+        final String packageName = attributionSource.getPackageName();
+        final int uid = attributionSource.getUid();
+        final String attributionTag = attributionSource.getAttributionTag();
         verifyIncomingUid(uid);
         verifyIncomingOp(code);
         if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) {
@@ -5103,8 +5190,13 @@
                     }
 
                     if (shell.packageName != null) {
-                        shell.mInterface.startOperation(shell.mToken, shell.op, shell.packageUid,
-                                shell.packageName, shell.attributionTag, true, true,
+                        final AttributionSource shellAttributionSource =
+                                new AttributionSource.Builder(shell.packageUid)
+                                        .setPackageName(shell.packageName)
+                                        .setAttributionTag(shell.attributionTag)
+                                        .build();
+                        shell.mInterface.startOperationWithState(shell.mToken, shell.op,
+                                shellAttributionSource.asState(), true, true,
                                 "appops start shell command", true,
                                 AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR, ATTRIBUTION_CHAIN_ID_NONE);
                     } else {
@@ -5119,8 +5211,13 @@
                     }
 
                     if (shell.packageName != null) {
-                        shell.mInterface.finishOperation(shell.mToken, shell.op, shell.packageUid,
-                                shell.packageName, shell.attributionTag);
+                        final AttributionSource shellAttributionSource =
+                                new AttributionSource.Builder(shell.packageUid)
+                                        .setPackageName(shell.packageName)
+                                        .setAttributionTag(shell.attributionTag)
+                                        .build();
+                        shell.mInterface.finishOperationWithState(shell.mToken, shell.op,
+                                shellAttributionSource.asState());
                     } else {
                         return -1;
                     }
@@ -6666,25 +6763,24 @@
             return mCheckOpsDelegate;
         }
 
-        public int checkOperation(int code, int uid, String packageName,
-                @Nullable String attributionTag, boolean raw) {
+        public int checkOperation(int code, AttributionSource attributionSource, boolean raw) {
             if (mPolicy != null) {
                 if (mCheckOpsDelegate != null) {
-                    return mPolicy.checkOperation(code, uid, packageName, attributionTag, raw,
+                    return mPolicy.checkOperation(code, attributionSource, raw,
                             this::checkDelegateOperationImpl);
                 } else {
-                    return mPolicy.checkOperation(code, uid, packageName, attributionTag, raw,
+                    return mPolicy.checkOperation(code, attributionSource, raw,
                             AppOpsService.this::checkOperationImpl);
                 }
             } else if (mCheckOpsDelegate != null) {
-                return checkDelegateOperationImpl(code, uid, packageName, attributionTag, raw);
+                return checkDelegateOperationImpl(code, attributionSource, raw);
             }
-            return checkOperationImpl(code, uid, packageName, attributionTag, raw);
+            return checkOperationImpl(code, attributionSource, raw);
         }
 
-        private int checkDelegateOperationImpl(int code, int uid, String packageName,
-                @Nullable String attributionTag, boolean raw) {
-            return mCheckOpsDelegate.checkOperation(code, uid, packageName, attributionTag, raw,
+        private int checkDelegateOperationImpl(int code, AttributionSource attributionSource,
+                boolean raw) {
+            return mCheckOpsDelegate.checkOperation(code, attributionSource, raw,
                     AppOpsService.this::checkOperationImpl);
         }
 
@@ -6709,32 +6805,32 @@
                     AppOpsService.this::checkAudioOperationImpl);
         }
 
-        public SyncNotedAppOp noteOperation(int code, int uid, String packageName,
-                String attributionTag, boolean shouldCollectAsyncNotedOp, String message,
+        public SyncNotedAppOp noteOperation(int code, AttributionSource attributionSource,
+                boolean shouldCollectAsyncNotedOp, String message,
                 boolean shouldCollectMessage) {
             if (mPolicy != null) {
                 if (mCheckOpsDelegate != null) {
-                    return mPolicy.noteOperation(code, uid, packageName, attributionTag,
+                    return mPolicy.noteOperation(code, attributionSource,
                             shouldCollectAsyncNotedOp, message, shouldCollectMessage,
                             this::noteDelegateOperationImpl);
                 } else {
-                    return mPolicy.noteOperation(code, uid, packageName, attributionTag,
+                    return mPolicy.noteOperation(code, attributionSource,
                             shouldCollectAsyncNotedOp, message, shouldCollectMessage,
                             AppOpsService.this::noteOperationImpl);
                 }
             } else if (mCheckOpsDelegate != null) {
-                return noteDelegateOperationImpl(code, uid, packageName,
-                        attributionTag, shouldCollectAsyncNotedOp, message, shouldCollectMessage);
+                return noteDelegateOperationImpl(code, attributionSource, shouldCollectAsyncNotedOp,
+                        message, shouldCollectMessage);
             }
-            return noteOperationImpl(code, uid, packageName, attributionTag,
+            return noteOperationImpl(code, attributionSource,
                     shouldCollectAsyncNotedOp, message, shouldCollectMessage);
         }
 
-        private SyncNotedAppOp noteDelegateOperationImpl(int code, int uid,
-                @Nullable String packageName, @Nullable String featureId,
+        private SyncNotedAppOp noteDelegateOperationImpl(int code,
+                AttributionSource attributionSource,
                 boolean shouldCollectAsyncNotedOp, @Nullable String message,
                 boolean shouldCollectMessage) {
-            return mCheckOpsDelegate.noteOperation(code, uid, packageName, featureId,
+            return mCheckOpsDelegate.noteOperation(code, attributionSource,
                     shouldCollectAsyncNotedOp, message, shouldCollectMessage,
                     AppOpsService.this::noteOperationImpl);
         }
@@ -6770,39 +6866,38 @@
                     AppOpsService.this::noteProxyOperationImpl);
         }
 
-        public SyncNotedAppOp startOperation(IBinder token, int code, int uid,
-                @Nullable String packageName, @NonNull String attributionTag,
-                boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp,
-                @Nullable String message, boolean shouldCollectMessage,
-                @AttributionFlags int attributionFlags, int attributionChainId) {
+        public SyncNotedAppOp startOperation(IBinder token, int code,
+                AttributionSource attributionSource, boolean startIfModeDefault,
+                boolean shouldCollectAsyncNotedOp, @Nullable String message,
+                boolean shouldCollectMessage, @AttributionFlags int attributionFlags,
+                int attributionChainId) {
             if (mPolicy != null) {
                 if (mCheckOpsDelegate != null) {
-                    return mPolicy.startOperation(token, code, uid, packageName,
-                            attributionTag, startIfModeDefault, shouldCollectAsyncNotedOp, message,
+                    return mPolicy.startOperation(token, code, attributionSource,
+                            startIfModeDefault, shouldCollectAsyncNotedOp, message,
                             shouldCollectMessage, attributionFlags, attributionChainId,
                             this::startDelegateOperationImpl);
                 } else {
-                    return mPolicy.startOperation(token, code, uid, packageName, attributionTag,
+                    return mPolicy.startOperation(token, code, attributionSource,
                             startIfModeDefault, shouldCollectAsyncNotedOp, message,
                             shouldCollectMessage, attributionFlags, attributionChainId,
                             AppOpsService.this::startOperationImpl);
                 }
             } else if (mCheckOpsDelegate != null) {
-                return startDelegateOperationImpl(token, code, uid, packageName, attributionTag,
+                return startDelegateOperationImpl(token, code, attributionSource,
                         startIfModeDefault, shouldCollectAsyncNotedOp, message,
                         shouldCollectMessage, attributionFlags, attributionChainId);
             }
-            return startOperationImpl(token, code, uid, packageName, attributionTag,
+            return startOperationImpl(token, code, attributionSource,
                     startIfModeDefault, shouldCollectAsyncNotedOp, message, shouldCollectMessage,
                     attributionFlags, attributionChainId);
         }
 
-        private SyncNotedAppOp startDelegateOperationImpl(IBinder token, int code, int uid,
-                @Nullable String packageName, @Nullable String attributionTag,
-                boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, String message,
-                boolean shouldCollectMessage, @AttributionFlags int attributionFlags,
-                int attributionChainId) {
-            return mCheckOpsDelegate.startOperation(token, code, uid, packageName, attributionTag,
+        private SyncNotedAppOp startDelegateOperationImpl(IBinder token, int code,
+                AttributionSource attributionSource, boolean startIfModeDefault,
+                boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
+                @AttributionFlags int attributionFlags, int attributionChainId) {
+            return mCheckOpsDelegate.startOperation(token, code, attributionSource,
                     startIfModeDefault, shouldCollectAsyncNotedOp, message, shouldCollectMessage,
                     attributionFlags, attributionChainId, AppOpsService.this::startOperationImpl);
         }
@@ -6848,26 +6943,26 @@
                     attributionChainId, AppOpsService.this::startProxyOperationImpl);
         }
 
-        public void finishOperation(IBinder clientId, int code, int uid, String packageName,
-                String attributionTag) {
+        public void finishOperation(IBinder clientId, int code,
+                AttributionSource attributionSource) {
             if (mPolicy != null) {
                 if (mCheckOpsDelegate != null) {
-                    mPolicy.finishOperation(clientId, code, uid, packageName, attributionTag,
+                    mPolicy.finishOperation(clientId, code, attributionSource,
                             this::finishDelegateOperationImpl);
                 } else {
-                    mPolicy.finishOperation(clientId, code, uid, packageName, attributionTag,
+                    mPolicy.finishOperation(clientId, code, attributionSource,
                             AppOpsService.this::finishOperationImpl);
                 }
             } else if (mCheckOpsDelegate != null) {
-                finishDelegateOperationImpl(clientId, code, uid, packageName, attributionTag);
+                finishDelegateOperationImpl(clientId, code, attributionSource);
             } else {
-                finishOperationImpl(clientId, code, uid, packageName, attributionTag);
+                finishOperationImpl(clientId, code, attributionSource);
             }
         }
 
-        private void finishDelegateOperationImpl(IBinder clientId, int code, int uid,
-                String packageName, String attributionTag) {
-            mCheckOpsDelegate.finishOperation(clientId, code, uid, packageName, attributionTag,
+        private void finishDelegateOperationImpl(IBinder clientId, int code,
+                AttributionSource attributionSource) {
+            mCheckOpsDelegate.finishOperation(clientId, code, attributionSource,
                     AppOpsService.this::finishOperationImpl);
         }
 
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index 7f3ea6a..0d3e0bc 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -77,8 +77,8 @@
             Flags::enablePowerThrottlingClamper);
 
     private final FlagState mSmallAreaDetectionFlagState = new FlagState(
-            Flags.FLAG_ENABLE_SMALL_AREA_DETECTION,
-            Flags::enableSmallAreaDetection);
+            com.android.graphics.surfaceflinger.flags.Flags.FLAG_ENABLE_SMALL_AREA_DETECTION,
+            com.android.graphics.surfaceflinger.flags.Flags::enableSmallAreaDetection);
 
     /** Returns whether connected display management is enabled or not. */
     public boolean isConnectedDisplayManagementEnabled() {
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index 9141814..9ab9c9d 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -104,12 +104,3 @@
     bug: "211737588"
     is_fixed_read_only: true
 }
-
-flag {
-    name: "enable_small_area_detection"
-    namespace: "display_manager"
-    description: "Feature flag for SmallAreaDetection"
-    bug: "298722189"
-    is_fixed_read_only: true
-}
-
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index 08503cb..3e46ee2 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -50,6 +50,8 @@
     /**
      * Called by the power manager to tell the input method manager whether it
      * should start watching for wake events.
+     *
+     * @param interactive the interactive mode parameter
      */
     public abstract void setInteractive(boolean interactive);
 
@@ -61,16 +63,16 @@
     /**
      * Returns the list of installed input methods for the specified user.
      *
-     * @param userId The user ID to be queried.
-     * @return A list of {@link InputMethodInfo}.  VR-only IMEs are already excluded.
+     * @param userId the user ID to be queried
+     * @return a list of {@link InputMethodInfo}. VR-only IMEs are already excluded
      */
     public abstract List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId);
 
     /**
      * Returns the list of installed input methods that are enabled for the specified user.
      *
-     * @param userId The user ID to be queried.
-     * @return A list of {@link InputMethodInfo} that are enabled for {@code userId}.
+     * @param userId the user ID to be queried
+     * @return a list of {@link InputMethodInfo} that are enabled for {@code userId}
      */
     public abstract List<InputMethodInfo> getEnabledInputMethodListAsUser(@UserIdInt int userId);
 
@@ -78,8 +80,10 @@
      * Called by the Autofill Frameworks to request an {@link InlineSuggestionsRequest} from
      * the input method.
      *
+     * @param userId      the user ID to be queried
      * @param requestInfo information needed to create an {@link InlineSuggestionsRequest}.
-     * @param cb {@link IInlineSuggestionsRequestCallback} used to pass back the request object.
+     * @param cb          {@link IInlineSuggestionsRequestCallback} used to pass back the request
+     *                    object
      */
     public abstract void onCreateInlineSuggestionsRequest(@UserIdInt int userId,
             InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback cb);
@@ -88,8 +92,8 @@
      * Force switch to the enabled input method by {@code imeId} for current user. If the input
      * method with {@code imeId} is not enabled or not installed, do nothing.
      *
-     * @param imeId  The input method ID to be switched to.
-     * @param userId The user ID to be queried.
+     * @param imeId  the input method ID to be switched to
+     * @param userId the user ID to be queried
      * @return {@code true} if the current input method was successfully switched to the input
      * method by {@code imeId}; {@code false} the input method with {@code imeId} is not available
      * to be switched.
@@ -100,28 +104,30 @@
      * Force enable or disable the input method associated with {@code imeId} for given user. If
      * the input method associated with {@code imeId} is not installed, do nothing.
      *
-     * @param imeId  The input method ID to be enabled or disabled.
+     * @param imeId   the input method ID to be enabled or disabled
      * @param enabled {@code true} if the input method associated with {@code imeId} should be
-     *                enabled.
-     * @param userId The user ID to be queried.
+     *                enabled
+     * @param userId  the user ID to be queried
      * @return {@code true} if the input method associated with {@code imeId} was successfully
-     *         enabled or disabled, {@code false} if the input method specified is not installed
-     *         or was unable to be enabled/disabled for some other reason.
+     * enabled or disabled, {@code false} if the input method specified is not installed
+     * or was unable to be enabled/disabled for some other reason.
      */
     public abstract boolean setInputMethodEnabled(String imeId, boolean enabled,
             @UserIdInt int userId);
 
     /**
      * Registers a new {@link InputMethodListListener}.
+     *
+     * @param listener the listener to add
      */
     public abstract void registerInputMethodListListener(InputMethodListListener listener);
 
     /**
      * Transfers input focus from a given input token to that of the IME window.
      *
-     * @param sourceInputToken The source token.
-     * @param displayId The display hosting the IME window.
-     * @return {@code true} if the transfer is successful.
+     * @param sourceInputToken the source token.
+     * @param displayId        the display hosting the IME window
+     * @return {@code true} if the transfer is successful
      */
     public abstract boolean transferTouchFocusToImeWindow(@NonNull IBinder sourceInputToken,
             int displayId);
@@ -132,7 +138,7 @@
      * or SystemUI).
      *
      * @param windowToken the window token that is now in control, or {@code null} if no client
-     *                   window is in control of the IME.
+     *                    window is in control of the IME
      */
     public abstract void reportImeControl(@Nullable IBinder windowToken);
 
@@ -163,8 +169,8 @@
      * Callback when the IInputMethodSession from the accessibility service with the specified
      * accessibilityConnectionId is created.
      *
-     * @param accessibilityConnectionId The connection id of the accessibility service.
-     * @param session The session passed back from the accessibility service.
+     * @param accessibilityConnectionId the connection id of the accessibility service
+     * @param session                   the session passed back from the accessibility service
      */
     public abstract void onSessionForAccessibilityCreated(int accessibilityConnectionId,
             IAccessibilityInputMethodSession session);
@@ -173,7 +179,7 @@
      * Unbind the accessibility service with the specified accessibilityConnectionId from current
      * client.
      *
-     * @param accessibilityConnectionId The connection id of the accessibility service.
+     * @param accessibilityConnectionId the connection id of the accessibility service
      */
     public abstract void unbindAccessibilityFromCurrentClient(int accessibilityConnectionId);
 
@@ -181,7 +187,7 @@
      * Switch the keyboard layout in response to a keyboard shortcut.
      *
      * @param direction {@code 1} to switch to the next subtype, {@code -1} to switch to the
-     *                           previous subtype.
+     *                  previous subtype
      */
     public abstract void switchKeyboardLayout(int direction);
 
@@ -192,7 +198,7 @@
     public abstract boolean isAnyInputConnectionActive();
 
     /**
-     * Fake implementation of {@link InputMethodManagerInternal}.  All the methods do nothing.
+     * Fake implementation of {@link InputMethodManagerInternal}. All the methods do nothing.
      */
     private static final InputMethodManagerInternal NOP =
             new InputMethodManagerInternal() {
@@ -282,7 +288,7 @@
             };
 
     /**
-     * @return Global instance if exists.  Otherwise, a fallback no-op instance.
+     * @return Global instance if exists. Otherwise, a fallback no-op instance.
      */
     @NonNull
     public static InputMethodManagerInternal get() {
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 9dec1df..d456a74 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -192,6 +192,7 @@
 
     // Start of methods that implement MediaRouter2 operations.
 
+    @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
     @NonNull
     public boolean verifyPackageExists(@NonNull String clientPackageName) {
         final int pid = Binder.getCallingPid();
@@ -199,11 +200,7 @@
         final long token = Binder.clearCallingIdentity();
 
         try {
-            mContext.enforcePermission(
-                    Manifest.permission.MEDIA_CONTENT_CONTROL,
-                    pid,
-                    uid,
-                    "Must hold MEDIA_CONTENT_CONTROL permission.");
+            enforcePrivilegedRoutingPermissions(uid, pid);
             PackageManager pm = mContext.getPackageManager();
             pm.getPackageInfo(clientPackageName, PackageManager.PackageInfoFlags.of(0));
             return true;
@@ -482,6 +479,7 @@
         }
     }
 
+    @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
     public void registerManager(@NonNull IMediaRouter2Manager manager,
             @NonNull String callerPackageName) {
         Objects.requireNonNull(manager, "manager must not be null");
@@ -729,6 +727,15 @@
         return hasBluetoothRoutingPermission;
     }
 
+    @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
+    private void enforcePrivilegedRoutingPermissions(int callerUid, int callerPid) {
+        mContext.enforcePermission(
+                Manifest.permission.MEDIA_CONTENT_CONTROL,
+                callerPid,
+                callerUid,
+                "Must hold MEDIA_CONTENT_CONTROL permission.");
+    }
+
     // End of methods that implements operations for both MediaRouter2 and MediaRouter2Manager.
 
     public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
@@ -837,15 +844,18 @@
     private void unregisterRouter2Locked(@NonNull IMediaRouter2 router, boolean died) {
         RouterRecord routerRecord = mAllRouterRecords.remove(router.asBinder());
         if (routerRecord == null) {
-            Slog.w(TAG, "Ignoring unregistering unknown router2");
+            Slog.w(
+                    TAG,
+                    TextUtils.formatSimple(
+                            "Ignoring unregistering unknown router: %s, died: %b", router, died));
             return;
         }
 
         Slog.i(
                 TAG,
                 TextUtils.formatSimple(
-                        "unregisterRouter2 | package: %s, router id: %d",
-                        routerRecord.mPackageName, routerRecord.mRouterId));
+                        "unregisterRouter2 | package: %s, router id: %d, died: %b",
+                        routerRecord.mPackageName, routerRecord.mRouterId, died));
 
         UserRecord userRecord = routerRecord.mUserRecord;
         userRecord.mRouterRecords.remove(routerRecord);
@@ -1161,6 +1171,7 @@
         return sessionInfos;
     }
 
+    @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
     @GuardedBy("mLock")
     private void registerManagerLocked(
             @NonNull IMediaRouter2Manager manager,
@@ -1184,8 +1195,7 @@
                             + " callerUserId: %d",
                         callerUid, callerPid, callerPackageName, callerUserId));
 
-        mContext.enforcePermission(Manifest.permission.MEDIA_CONTENT_CONTROL, callerPid, callerUid,
-                "Must hold MEDIA_CONTENT_CONTROL permission.");
+        enforcePrivilegedRoutingPermissions(callerUid, callerPid);
 
         UserRecord userRecord = getOrCreateUserRecordLocked(callerUserId);
         managerRecord = new ManagerRecord(
@@ -1230,15 +1240,22 @@
     private void unregisterManagerLocked(@NonNull IMediaRouter2Manager manager, boolean died) {
         ManagerRecord managerRecord = mAllManagerRecords.remove(manager.asBinder());
         if (managerRecord == null) {
+            Slog.w(
+                    TAG,
+                    TextUtils.formatSimple(
+                            "Ignoring unregistering unknown manager: %s, died: %b", manager, died));
             return;
         }
         UserRecord userRecord = managerRecord.mUserRecord;
 
-        Slog.i(TAG, TextUtils.formatSimple(
-                "unregisterManager | package: %s, user: %d, manager: %d",
-                managerRecord.mOwnerPackageName,
-                userRecord.mUserId,
-                managerRecord.mManagerId));
+        Slog.i(
+                TAG,
+                TextUtils.formatSimple(
+                        "unregisterManager | package: %s, user: %d, manager: %d, died: %b",
+                        managerRecord.mOwnerPackageName,
+                        userRecord.mUserId,
+                        managerRecord.mManagerId,
+                        died));
 
         userRecord.mManagerRecords.remove(managerRecord);
         managerRecord.dispose();
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index 44719f8..6df4a95 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -409,6 +409,7 @@
     }
 
     // Binder call
+    @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
     @Override
     public boolean verifyPackageExists(String clientPackageName) {
         return mService2.verifyPackageExists(clientPackageName);
@@ -536,6 +537,7 @@
     }
 
     // Binder call
+    @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
     @Override
     public void registerManager(IMediaRouter2Manager manager, String callerPackageName) {
         final int uid = Binder.getCallingUid();
diff --git a/services/core/java/com/android/server/media/projection/FrameworkStatsLogWrapper.java b/services/core/java/com/android/server/media/projection/FrameworkStatsLogWrapper.java
index 5bad067..6c74cba 100644
--- a/services/core/java/com/android/server/media/projection/FrameworkStatsLogWrapper.java
+++ b/services/core/java/com/android/server/media/projection/FrameworkStatsLogWrapper.java
@@ -21,8 +21,8 @@
 /** Wrapper around {@link FrameworkStatsLog} */
 public class FrameworkStatsLogWrapper {
 
-    /** Wrapper around {@link FrameworkStatsLog#write}. */
-    public void write(
+    /** Wrapper around {@link FrameworkStatsLog#write} for MediaProjectionStateChanged atom. */
+    public void writeStateChanged(
             int code,
             int sessionId,
             int state,
@@ -41,4 +41,21 @@
                 timeSinceLastActive,
                 creationSource);
     }
+
+    /** Wrapper around {@link FrameworkStatsLog#write} for MediaProjectionTargetChanged atom. */
+    public void writeTargetChanged(
+            int code,
+            int sessionId,
+            int targetType,
+            int hostUid,
+            int targetUid,
+            int windowingMode) {
+        FrameworkStatsLog.write(
+                code,
+                sessionId,
+                targetType,
+                hostUid,
+                targetUid,
+                windowingMode);
+    }
 }
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index 893ed61..6deda46 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -479,6 +479,18 @@
         mMediaProjectionMetricsLogger.logAppSelectorDisplayed(hostUid);
     }
 
+    @VisibleForTesting
+    void notifyWindowingModeChanged(int contentToRecord, int targetUid, int windowingMode) {
+        synchronized (mLock) {
+            if (mProjectionGrant == null) {
+                Slog.i(TAG, "Cannot log MediaProjectionTargetChanged atom due to null projection");
+            } else {
+                mMediaProjectionMetricsLogger.logChangedWindowingMode(
+                        contentToRecord, mProjectionGrant.uid, targetUid, windowingMode);
+            }
+        }
+    }
+
     /**
      * Handles result of dialog shown from
      * {@link BinderService#buildReviewGrantedConsentIntentLocked()}.
@@ -905,6 +917,20 @@
         }
 
         @Override // Binder call
+        @EnforcePermission(MANAGE_MEDIA_PROJECTION)
+        public void notifyWindowingModeChanged(
+                int contentToRecord, int targetUid, int windowingMode) {
+            notifyWindowingModeChanged_enforcePermission();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                MediaProjectionManagerService.this.notifyWindowingModeChanged(
+                        contentToRecord, targetUid, windowingMode);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override // Binder call
         public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
             if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
             final long token = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionMetricsLogger.java b/services/core/java/com/android/server/media/projection/MediaProjectionMetricsLogger.java
index d7fefeb..be2a25a 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionMetricsLogger.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionMetricsLogger.java
@@ -16,16 +16,32 @@
 
 package com.android.server.media.projection;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.view.ContentRecordingSession.RECORD_CONTENT_DISPLAY;
+import static android.view.ContentRecordingSession.RECORD_CONTENT_TASK;
+
 import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN;
 import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_APP_SELECTOR_DISPLAYED;
 import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_CAPTURING_IN_PROGRESS;
 import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_INITIATED;
 import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_PERMISSION_REQUEST_DISPLAYED;
 import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_STOPPED;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_APP_TASK;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_DISPLAY;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_UNKNOWN;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_FREEFORM;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_FULLSCREEN;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_SPLIT_SCREEN;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_UNKNOWN;
 
+import android.app.WindowConfiguration.WindowingMode;
 import android.content.Context;
 import android.util.Log;
+import android.view.ContentRecordingSession.RecordContent;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.FrameworkStatsLog;
 
 import java.time.Duration;
@@ -91,7 +107,7 @@
                 durationSinceLastActiveSession == null
                         ? TIME_SINCE_LAST_ACTIVE_UNKNOWN
                         : (int) durationSinceLastActiveSession.toSeconds();
-        write(
+        writeStateChanged(
                 mSessionIdGenerator.createAndGetNewSessionId(),
                 MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_INITIATED,
                 hostUid,
@@ -102,13 +118,13 @@
 
     /**
      * Logs that the user entered the setup flow and permission dialog is displayed. This state is
-     * not sent when the permission is already granted and we skipped showing the permission dialog.
+     * not sent when the permission is already granted, and we skipped showing the permission dialog.
      *
      * @param hostUid UID of the package that initiates MediaProjection.
      */
     public void logPermissionRequestDisplayed(int hostUid) {
         Log.d(TAG, "logPermissionRequestDisplayed");
-        write(
+        writeStateChanged(
                 mSessionIdGenerator.getCurrentSessionId(),
                 MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_PERMISSION_REQUEST_DISPLAYED,
                 hostUid,
@@ -123,7 +139,7 @@
      * @param hostUid UID of the package that initiates MediaProjection.
      */
     public void logProjectionPermissionRequestCancelled(int hostUid) {
-        write(
+        writeStateChanged(
                 mSessionIdGenerator.getCurrentSessionId(),
                 FrameworkStatsLog
                         .MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_CANCELLED,
@@ -141,7 +157,7 @@
      */
     public void logAppSelectorDisplayed(int hostUid) {
         Log.d(TAG, "logAppSelectorDisplayed");
-        write(
+        writeStateChanged(
                 mSessionIdGenerator.getCurrentSessionId(),
                 MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_APP_SELECTOR_DISPLAYED,
                 hostUid,
@@ -158,7 +174,7 @@
      */
     public void logInProgress(int hostUid, int targetUid) {
         Log.d(TAG, "logInProgress");
-        write(
+        writeStateChanged(
                 mSessionIdGenerator.getCurrentSessionId(),
                 MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_CAPTURING_IN_PROGRESS,
                 hostUid,
@@ -168,6 +184,54 @@
     }
 
     /**
+     * Logs that the windowing mode of a projection has changed.
+     *
+     * @param contentToRecord ContentRecordingSession.RecordContent indicating whether it is a
+     *                        task capture or display capture - gets converted to the corresponding
+     *                        TargetType before being logged.
+     * @param hostUid UID of the package that initiates MediaProjection.
+     * @param targetUid UID of the package that is captured if selected.
+     * @param windowingMode Updated WindowConfiguration.WindowingMode of the captured region - gets
+     *                      converted to the corresponding TargetWindowingMode before being logged.
+     */
+    public void logChangedWindowingMode(
+            int contentToRecord, int hostUid, int targetUid, int windowingMode) {
+        Log.d(TAG, "logChangedWindowingMode");
+        writeTargetChanged(
+                mSessionIdGenerator.getCurrentSessionId(),
+                contentToRecordToTargetType(contentToRecord),
+                hostUid,
+                targetUid,
+                windowingModeToTargetWindowingMode(windowingMode));
+
+    }
+
+    @VisibleForTesting
+    public int contentToRecordToTargetType(@RecordContent int recordContentType) {
+        return switch (recordContentType) {
+            case RECORD_CONTENT_DISPLAY ->
+                    MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_DISPLAY;
+            case RECORD_CONTENT_TASK ->
+                    MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_APP_TASK;
+            default -> MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_UNKNOWN;
+        };
+    }
+
+    @VisibleForTesting
+    public int windowingModeToTargetWindowingMode(@WindowingMode int windowingMode) {
+        return switch (windowingMode) {
+            case WINDOWING_MODE_FULLSCREEN ->
+                    MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_FULLSCREEN;
+            case WINDOWING_MODE_FREEFORM ->
+                    MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_FREEFORM;
+            case WINDOWING_MODE_MULTI_WINDOW ->
+                    MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_SPLIT_SCREEN;
+            default ->
+                    MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_UNKNOWN;
+        };
+    }
+
+    /**
      * Logs that the capturing stopped, either normally or because of error.
      *
      * @param hostUid UID of the package that initiates MediaProjection.
@@ -178,7 +242,7 @@
                 mPreviousState
                         == MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_CAPTURING_IN_PROGRESS;
         Log.d(TAG, "logStopped: wasCaptureInProgress=" + wasCaptureInProgress);
-        write(
+        writeStateChanged(
                 mSessionIdGenerator.getCurrentSessionId(),
                 MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_STOPPED,
                 hostUid,
@@ -191,14 +255,31 @@
         }
     }
 
-    private void write(
+    public void notifyProjectionStateChange(int hostUid, int state, int sessionCreationSource) {
+        writeStateChanged(hostUid, state, sessionCreationSource);
+    }
+
+    private void writeStateChanged(int hostUid, int state, int sessionCreationSource) {
+        mFrameworkStatsLogWrapper.writeStateChanged(
+                /* code */ FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED,
+                /* session_id */ 123,
+                /* state */ state,
+                /* previous_state */ FrameworkStatsLog
+                        .MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_UNKNOWN,
+                /* host_uid */ hostUid,
+                /* target_uid */ -1,
+                /* time_since_last_active */ 0,
+                /* creation_source */ sessionCreationSource);
+    }
+
+    private void writeStateChanged(
             int sessionId,
             int state,
             int hostUid,
             int targetUid,
             int timeSinceLastActive,
             int creationSource) {
-        mFrameworkStatsLogWrapper.write(
+        mFrameworkStatsLogWrapper.writeStateChanged(
                 /* code */ FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED,
                 sessionId,
                 state,
@@ -209,4 +290,19 @@
                 creationSource);
         mPreviousState = state;
     }
+
+    private void writeTargetChanged(
+            int sessionId,
+            int targetType,
+            int hostUid,
+            int targetUid,
+            int targetWindowingMode) {
+        mFrameworkStatsLogWrapper.writeTargetChanged(
+                /* code */ FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED,
+                sessionId,
+                targetType,
+                hostUid,
+                targetUid,
+                targetWindowingMode);
+    }
 }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 4b5d52f..b2f00a2 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -291,6 +291,7 @@
 import com.android.internal.compat.IPlatformCompat;
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
 import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
+import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags;
 import com.android.internal.logging.InstanceId;
 import com.android.internal.logging.InstanceIdSequence;
 import com.android.internal.logging.MetricsLogger;
@@ -2909,7 +2910,16 @@
             mPreferencesHelper.updateFixedImportance(mUm.getUsers());
             mPreferencesHelper.migrateNotificationPermissions(mUm.getUsers());
         } else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
-            if (expireBitmaps()) {
+            if (mFlagResolver.isEnabled(NotificationFlags.DEBUG_SHORT_BITMAP_DURATION)) {
+                new Thread(() -> {
+                    while (true) {
+                        try {
+                            Thread.sleep(5000);
+                        } catch (InterruptedException e) { }
+                        mInternalService.removeBitmaps();
+                    }
+                }).start();
+            } else if (expireBitmaps()) {
                 NotificationBitmapJobService.scheduleJob(getContext());
             }
         }
@@ -6765,7 +6775,14 @@
                     final long timePostedMs = r.getSbn().getPostTime();
                     final long timeNowMs = System.currentTimeMillis();
 
-                    if (isBitmapExpired(timePostedMs, timeNowMs, BITMAP_DURATION.toMillis())) {
+                    final long bitmapDuration;
+                    if (mFlagResolver.isEnabled(NotificationFlags.DEBUG_SHORT_BITMAP_DURATION)) {
+                        bitmapDuration = Duration.ofSeconds(5).toMillis();
+                    } else {
+                        bitmapDuration = BITMAP_DURATION.toMillis();
+                    }
+
+                    if (isBitmapExpired(timePostedMs, timeNowMs, bitmapDuration)) {
                         removeBitmapAndRepost(r);
                     }
                 }
diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig
index 66a76cc..25b7ca1 100644
--- a/services/core/java/com/android/server/notification/flags.aconfig
+++ b/services/core/java/com/android/server/notification/flags.aconfig
@@ -7,12 +7,7 @@
   bug: "290381858"
 }
 
-flag {
-  name: "modes_api"
-  namespace: "systemui"
-  description: "This flag controls new and updated DND apis"
-  bug: "300477976"
-}
+
 
 flag {
   name: "polite_notifications"
diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java
index 42a97f7..5cd6287 100644
--- a/services/core/java/com/android/server/pm/PackageArchiver.java
+++ b/services/core/java/com/android/server/pm/PackageArchiver.java
@@ -17,8 +17,8 @@
 package com.android.server.pm;
 
 import static android.app.ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED;
-import static android.content.pm.ArchivedActivity.bytesFromBitmap;
-import static android.content.pm.ArchivedActivity.drawableToBitmap;
+import static android.content.pm.ArchivedActivityInfo.bytesFromBitmap;
+import static android.content.pm.ArchivedActivityInfo.drawableToBitmap;
 import static android.content.pm.PackageManager.DELETE_ARCHIVE;
 import static android.content.pm.PackageManager.DELETE_KEEP_DATA;
 import static android.os.PowerExemptionManager.REASON_PACKAGE_UNARCHIVE;
@@ -46,6 +46,12 @@
 import android.content.pm.VersionedPackage;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.graphics.Color;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Environment;
@@ -56,6 +62,7 @@
 import android.text.TextUtils;
 import android.util.Slog;
 
+import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.pm.pkg.ArchiveState;
@@ -367,7 +374,7 @@
         // TODO(b/298452477) Handle monochrome icons.
         // In the rare case the archived app defined more than two launcher activities, we choose
         // the first one arbitrarily.
-        return decodeIcon(activityInfos.get(0));
+        return includeCloudOverlay(decodeIcon(activityInfos.get(0)));
     }
 
     @VisibleForTesting
@@ -375,6 +382,34 @@
         return BitmapFactory.decodeFile(archiveActivityInfo.getIconBitmap().toString());
     }
 
+    Bitmap includeCloudOverlay(Bitmap bitmap) {
+        Drawable cloudDrawable =
+                mContext.getResources()
+                        .getDrawable(R.drawable.archived_app_cloud_overlay, mContext.getTheme());
+        if (cloudDrawable == null) {
+            Slog.e(TAG, "Unable to locate cloud overlay for archived app!");
+            return bitmap;
+        }
+        BitmapDrawable appIconDrawable = new BitmapDrawable(mContext.getResources(), bitmap);
+        PorterDuffColorFilter colorFilter =
+                new PorterDuffColorFilter(
+                        Color.argb(0.32f /* alpha */, 0f /* red */, 0f /* green */, 0f /* blue */),
+                        PorterDuff.Mode.SRC_ATOP);
+        appIconDrawable.setColorFilter(colorFilter);
+        appIconDrawable.setBounds(
+                0 /* left */,
+                0 /* top */,
+                cloudDrawable.getIntrinsicWidth(),
+                cloudDrawable.getIntrinsicHeight());
+        LayerDrawable layerDrawable =
+                new LayerDrawable(new Drawable[] {appIconDrawable, cloudDrawable});
+        final int iconSize = mContext.getSystemService(
+                ActivityManager.class).getLauncherLargeIconSize();
+        Bitmap appIconWithCloudOverlay = drawableToBitmap(layerDrawable, iconSize);
+        bitmap.recycle();
+        return appIconWithCloudOverlay;
+    }
+
     private void verifyArchived(PackageStateInternal ps, int userId)
             throws PackageManager.NameNotFoundException {
         PackageUserStateInternal userState = ps.getUserStateOrDefault(userId);
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 3430bb4d..534f176 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1282,7 +1282,9 @@
      */
     private boolean isSameUserOrProfileGroupOrTargetIsCommunal(UserInfo asker, UserInfo target) {
         if (asker.id == target.id) return true;
-        if (target.isCommunalProfile()) return true;
+        if (android.multiuser.Flags.supportCommunalProfile()) {
+            if (target.isCommunalProfile()) return true;
+        }
         return (asker.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
                 && asker.profileGroupId == target.profileGroupId);
     }
diff --git a/services/core/java/com/android/server/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java
index b83421f..c2821ae 100644
--- a/services/core/java/com/android/server/policy/AppOpsPolicy.java
+++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java
@@ -50,11 +50,12 @@
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.function.HeptFunction;
 import com.android.internal.util.function.HexFunction;
+import com.android.internal.util.function.NonaFunction;
 import com.android.internal.util.function.QuadFunction;
-import com.android.internal.util.function.QuintConsumer;
 import com.android.internal.util.function.QuintFunction;
+import com.android.internal.util.function.TriConsumer;
+import com.android.internal.util.function.TriFunction;
 import com.android.internal.util.function.UndecFunction;
 import com.android.server.LocalServices;
 
@@ -229,10 +230,12 @@
     }
 
     @Override
-    public int checkOperation(int code, int uid, String packageName,
-            @Nullable String attributionTag, boolean raw,
-            QuintFunction<Integer, Integer, String, String, Boolean, Integer> superImpl) {
-        return superImpl.apply(code, resolveUid(code, uid), packageName, attributionTag, raw);
+    public int checkOperation(int code, AttributionSource attributionSource, boolean raw,
+            TriFunction<Integer, AttributionSource, Boolean, Integer> superImpl) {
+        final int uid = attributionSource.getUid();
+        final AttributionSource resolvedAttributionSource =
+                attributionSource.withUid(resolveUid(code, uid));
+        return superImpl.apply(code, resolvedAttributionSource, raw);
     }
 
     @Override
@@ -242,21 +245,25 @@
     }
 
     @Override
-    public SyncNotedAppOp noteOperation(int code, int uid, @Nullable String packageName,
-            @Nullable String attributionTag, boolean shouldCollectAsyncNotedOp, @Nullable
-            String message, boolean shouldCollectMessage, @NonNull HeptFunction<Integer, Integer,
-                    String, String, Boolean, String, Boolean, SyncNotedAppOp> superImpl) {
-        return superImpl.apply(resolveDatasourceOp(code, uid, packageName, attributionTag),
-                resolveUid(code, uid), packageName, attributionTag, shouldCollectAsyncNotedOp,
-                message, shouldCollectMessage);
+    public SyncNotedAppOp noteOperation(int code, AttributionSource attributionSource,
+            boolean shouldCollectAsyncNotedOp, @Nullable
+             String message, boolean shouldCollectMessage,
+            @NonNull QuintFunction<Integer, AttributionSource, Boolean, String, Boolean,
+                    SyncNotedAppOp> superImpl) {
+        final int uid = attributionSource.getUid();
+        final AttributionSource resolvedAttributionSource =
+                attributionSource.withUid(resolveUid(code, uid));
+        return superImpl.apply(resolveDatasourceOp(code, uid, attributionSource.getPackageName(),
+                attributionSource.getAttributionTag()), resolvedAttributionSource,
+                shouldCollectAsyncNotedOp, message, shouldCollectMessage);
     }
 
     @Override
     public SyncNotedAppOp noteProxyOperation(int code, @NonNull AttributionSource attributionSource,
             boolean shouldCollectAsyncNotedOp, @Nullable String message,
             boolean shouldCollectMessage, boolean skipProxyOperation, @NonNull HexFunction<Integer,
-                    AttributionSource, Boolean, String, Boolean, Boolean,
-                    SyncNotedAppOp> superImpl) {
+            AttributionSource, Boolean, String, Boolean, Boolean,
+            SyncNotedAppOp> superImpl) {
         return superImpl.apply(resolveDatasourceOp(code, attributionSource.getUid(),
                 attributionSource.getPackageName(), attributionSource.getAttributionTag()),
                 attributionSource, shouldCollectAsyncNotedOp, message, shouldCollectMessage,
@@ -264,17 +271,21 @@
     }
 
     @Override
-    public SyncNotedAppOp startOperation(IBinder token, int code, int uid,
-            @Nullable String packageName, @Nullable String attributionTag,
+    public SyncNotedAppOp startOperation(IBinder token, int code,
+            AttributionSource attributionSource,
             boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp, String message,
             boolean shouldCollectMessage, @AttributionFlags int attributionFlags,
-            int attributionChainId, @NonNull UndecFunction<IBinder, Integer, Integer, String,
-                    String, Boolean, Boolean, String, Boolean, Integer, Integer,
-            SyncNotedAppOp> superImpl) {
-        return superImpl.apply(token, resolveDatasourceOp(code, uid, packageName, attributionTag),
-                resolveUid(code, uid), packageName, attributionTag, startIfModeDefault,
-                shouldCollectAsyncNotedOp, message, shouldCollectMessage, attributionFlags,
-                attributionChainId);
+            int attributionChainId,
+            @NonNull NonaFunction<IBinder, Integer, AttributionSource, Boolean, Boolean, String,
+                    Boolean, Integer, Integer,
+                    SyncNotedAppOp> superImpl) {
+        final int uid = attributionSource.getUid();
+        final AttributionSource resolvedAttributionSource =
+                attributionSource.withUid(resolveUid(code, uid));
+        return superImpl.apply(token, resolveDatasourceOp(code, uid,
+                attributionSource.getPackageName(), attributionSource.getAttributionTag()),
+                resolvedAttributionSource, startIfModeDefault, shouldCollectAsyncNotedOp, message,
+                shouldCollectMessage, attributionFlags, attributionChainId);
     }
 
     @Override
@@ -293,11 +304,14 @@
     }
 
     @Override
-    public void finishOperation(IBinder clientId, int code, int uid, String packageName,
-            String attributionTag,
-            @NonNull QuintConsumer<IBinder, Integer, Integer, String, String> superImpl) {
-        superImpl.accept(clientId, resolveDatasourceOp(code, uid, packageName, attributionTag),
-                resolveUid(code, uid), packageName, attributionTag);
+    public void finishOperation(IBinder clientId, int code, AttributionSource attributionSource,
+            @NonNull TriConsumer<IBinder, Integer, AttributionSource> superImpl) {
+        final int uid = attributionSource.getUid();
+        final AttributionSource resolvedAttributionSource =
+                attributionSource.withUid(resolveUid(code, uid));
+        superImpl.accept(clientId, resolveDatasourceOp(code, uid,
+                 attributionSource.getPackageName(), attributionSource.getAttributionTag()),
+                 resolvedAttributionSource);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index b12ecc3..e4c7fc1 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -1166,7 +1166,11 @@
                                 .EXTERNAL_TV_INPUT_EVENT__EVENT_TYPE__CONNECTION_STATE_CHANGED,
                         mOnScreenInputId, mOnScreenSessionState);
             } else if (mOnScreenInputId != null) {
-                TvInputInfo currentInputInfo = userState.inputMap.get(mOnScreenInputId).info;
+                TvInputState currentInputState = userState.inputMap.get(mOnScreenInputId);
+                TvInputInfo currentInputInfo = null;
+                if (currentInputState != null) {
+                    currentInputInfo = currentInputState.info;
+                }
                 if (currentInputInfo != null && currentInputInfo.getHdmiDeviceInfo() != null
                         && inputId.equals(currentInputInfo.getParentId())) {
                     logExternalInputEvent(
diff --git a/services/core/java/com/android/server/wm/ContentRecorder.java b/services/core/java/com/android/server/wm/ContentRecorder.java
index 022ef61..8717098 100644
--- a/services/core/java/com/android/server/wm/ContentRecorder.java
+++ b/services/core/java/com/android/server/wm/ContentRecorder.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.Context.MEDIA_PROJECTION_SERVICE;
 import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
 import static android.view.ContentRecordingSession.RECORD_CONTENT_DISPLAY;
@@ -100,6 +101,8 @@
     @Configuration.Orientation
     private int mLastOrientation = ORIENTATION_UNDEFINED;
 
+    private int mLastWindowingMode = WINDOWING_MODE_UNDEFINED;
+
     private final boolean mCorrectForAnisotropicPixels;
 
     ContentRecorder(@NonNull DisplayContent displayContent) {
@@ -156,7 +159,8 @@
      * Handle a configuration change on the display content, and resize recording if needed.
      * @param lastOrientation the prior orientation of the configuration
      */
-    void onConfigurationChanged(@Configuration.Orientation int lastOrientation) {
+    void onConfigurationChanged(
+            @Configuration.Orientation int lastOrientation, int lastWindowingMode) {
         // Update surface for MediaProjection, if this DisplayContent is being used for recording.
         if (!isCurrentlyRecording() || mLastRecordedBounds == null) {
             return;
@@ -185,6 +189,16 @@
             }
         }
 
+        // Record updated windowing mode, if necessary.
+        int recordedContentWindowingMode = mRecordedWindowContainer.getWindowingMode();
+        if (lastWindowingMode != recordedContentWindowingMode) {
+            mMediaProjectionManager.notifyWindowingModeChanged(
+                    mContentRecordingSession.getContentToRecord(),
+                    mContentRecordingSession.getTargetUid(),
+                    recordedContentWindowingMode
+            );
+        }
+
         ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
                 "Content Recording: Display %d was already recording, so apply "
                         + "transformations if necessary",
@@ -327,8 +341,10 @@
             return;
         }
 
+        final int contentToRecord = mContentRecordingSession.getContentToRecord();
+
         // TODO(b/297514518) Do not start capture if the app is in PIP, the bounds are inaccurate.
-        if (mContentRecordingSession.getContentToRecord() == RECORD_CONTENT_TASK) {
+        if (contentToRecord == RECORD_CONTENT_TASK) {
             if (mRecordedWindowContainer.asTask().inPinnedWindowingMode()) {
                 ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
                         "Content Recording: Display %d should start recording, but "
@@ -375,7 +391,7 @@
 
         // Notify the client about the visibility of the mirrored region, now that we have begun
         // capture.
-        if (mContentRecordingSession.getContentToRecord() == RECORD_CONTENT_TASK) {
+        if (contentToRecord == RECORD_CONTENT_TASK) {
             mMediaProjectionManager.notifyActiveProjectionCapturedContentVisibilityChanged(
                     mRecordedWindowContainer.asTask().isVisibleRequested());
         } else {
@@ -385,6 +401,11 @@
                     currentDisplayState != DISPLAY_STATE_OFF);
         }
 
+        // Record initial windowing mode after recording starts.
+        mMediaProjectionManager.notifyWindowingModeChanged(
+                contentToRecord, mContentRecordingSession.getTargetUid(),
+                mRecordedWindowContainer.getWindowConfiguration().getWindowingMode());
+
         // No need to clean up. In SurfaceFlinger, parents hold references to their children. The
         // mirrored SurfaceControl is alive since the parent DisplayContent SurfaceControl is
         // holding a reference to it. Therefore, the mirrored SurfaceControl will be cleaned up
@@ -617,8 +638,9 @@
             Configuration mergedOverrideConfiguration) {
         WindowContainerListener.super.onMergedOverrideConfigurationChanged(
                 mergedOverrideConfiguration);
-        onConfigurationChanged(mLastOrientation);
+        onConfigurationChanged(mLastOrientation, mLastWindowingMode);
         mLastOrientation = mergedOverrideConfiguration.orientation;
+        mLastWindowingMode = mergedOverrideConfiguration.windowConfiguration.getWindowingMode();
     }
 
     // WindowContainerListener
@@ -635,6 +657,7 @@
         void stopActiveProjection();
         void notifyActiveProjectionCapturedContentResized(int width, int height);
         void notifyActiveProjectionCapturedContentVisibilityChanged(boolean isVisible);
+        void notifyWindowingModeChanged(int contentToRecord, int targetUid, int windowingMode);
     }
 
     private static final class RemoteMediaProjectionManagerWrapper implements
@@ -700,6 +723,22 @@
             }
         }
 
+        @Override
+        public void notifyWindowingModeChanged(int contentToRecord, int targetUid,
+                int windowingMode) {
+            fetchMediaProjectionManager();
+            if (mIMediaProjectionManager == null) {
+                return;
+            }
+            try {
+                mIMediaProjectionManager.notifyWindowingModeChanged(
+                        contentToRecord, targetUid, windowingMode);
+            } catch (RemoteException e) {
+                ProtoLog.e(WM_DEBUG_CONTENT_RECORDING,
+                        "Content Recording: Unable to tell log windowing mode change: %s", e);
+            }
+        }
+
         private void fetchMediaProjectionManager() {
             if (mIMediaProjectionManager != null) {
                 return;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 576e8a4..c716879 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -2757,6 +2757,7 @@
     @Override
     public void onConfigurationChanged(Configuration newParentConfig) {
         final int lastOrientation = getConfiguration().orientation;
+        final int lastWindowingMode = getWindowingMode();
         super.onConfigurationChanged(newParentConfig);
         if (mDisplayPolicy != null) {
             mDisplayPolicy.onConfigurationChanged();
@@ -2768,7 +2769,7 @@
 
         // Update surface for MediaProjection, if this DisplayContent is being used for recording.
         if (mContentRecorder != null) {
-            mContentRecorder.onConfigurationChanged(lastOrientation);
+            mContentRecorder.onConfigurationChanged(lastOrientation, lastWindowingMode);
         }
 
         if (lastOrientation != getConfiguration().orientation) {
diff --git a/services/core/java/com/android/server/wm/OWNERS b/services/core/java/com/android/server/wm/OWNERS
index f8c39d0..cd70447 100644
--- a/services/core/java/com/android/server/wm/OWNERS
+++ b/services/core/java/com/android/server/wm/OWNERS
@@ -18,5 +18,8 @@
 yunfanc@google.com
 wilsonshih@google.com
 
-per-file BackgroundActivityStartController.java = set noparent
-per-file BackgroundActivityStartController.java = brufino@google.com, topjohnwu@google.com, achim@google.com, ogunwale@google.com, louischang@google.com, lus@google.com
+# Files related to background activity launches
+per-file Background*Start* = set noparent
+per-file Background*Start* = file:/BAL_OWNERS
+per-file Background*Start* = ogunwale@google.com, louischang@google.com
+
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 3775ccd..a756847 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -86,6 +86,7 @@
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.server.LocalServices;
 import com.android.server.wm.WindowManagerService.H;
+import com.android.window.flags.Flags;
 
 import java.io.PrintWriter;
 import java.util.Collections;
@@ -166,8 +167,8 @@
         mCanSetUnrestrictedGestureExclusion =
                 service.mContext.checkCallingOrSelfPermission(SET_UNRESTRICTED_GESTURE_EXCLUSION)
                         == PERMISSION_GRANTED;
-        mCanAlwaysUpdateWallpaper =
-                service.mContext.checkCallingOrSelfPermission(ALWAYS_UPDATE_WALLPAPER)
+        mCanAlwaysUpdateWallpaper = Flags.alwaysUpdateWallpaperPermission()
+                && service.mContext.checkCallingOrSelfPermission(ALWAYS_UPDATE_WALLPAPER)
                         == PERMISSION_GRANTED;
         mShowingAlertWindowNotificationAllowed = mService.mShowAlertWindowNotifications;
         mDragDropController = mService.mDragDropController;
diff --git a/services/core/java/com/android/server/wm/SynchedDeviceConfig.java b/services/core/java/com/android/server/wm/SynchedDeviceConfig.java
index c2e819e..4d4f99f 100644
--- a/services/core/java/com/android/server/wm/SynchedDeviceConfig.java
+++ b/services/core/java/com/android/server/wm/SynchedDeviceConfig.java
@@ -20,7 +20,6 @@
 import android.provider.DeviceConfig;
 
 import java.util.Map;
-import java.util.Optional;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Executor;
 
@@ -98,27 +97,28 @@
      * @throws IllegalArgumentException {@code key} isn't recognised.
      */
     boolean getFlagValue(@NonNull String key) {
-        return findEntry(key).map(SynchedDeviceConfigEntry::getValue)
-                .orElseThrow(() -> new IllegalArgumentException("Unexpected flag name: " + key));
+        final SynchedDeviceConfigEntry entry = mDeviceConfigEntries.get(key);
+        if (entry == null) {
+            throw new IllegalArgumentException("Unexpected flag name: " + key);
+        }
+        return entry.getValue();
     }
 
     /**
      * @return {@code true} if the flag for the given {@code key} was enabled at build time.
      */
     boolean isBuildTimeFlagEnabled(@NonNull String key) {
-        return findEntry(key).map(SynchedDeviceConfigEntry::isBuildTimeFlagEnabled)
-                .orElseThrow(() -> new IllegalArgumentException("Unexpected flag name: " + key));
+        final SynchedDeviceConfigEntry entry = mDeviceConfigEntries.get(key);
+        if (entry == null) {
+            throw new IllegalArgumentException("Unexpected flag name: " + key);
+        }
+        return entry.isBuildTimeFlagEnabled();
     }
 
     private boolean isDeviceConfigFlagEnabled(@NonNull String key, boolean defaultValue) {
         return DeviceConfig.getBoolean(mNamespace, key, defaultValue);
     }
 
-    @NonNull
-    private Optional<SynchedDeviceConfigEntry> findEntry(@NonNull String key) {
-        return Optional.ofNullable(mDeviceConfigEntries.get(key));
-    }
-
     static class SynchedDeviceConfigBuilder {
 
         private final String mNamespace;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 6cad16c..5c5a1e1 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -6541,11 +6541,11 @@
                 mActivityType = ACTIVITY_TYPE_STANDARD;
             }
 
-            if (mActivityType != ACTIVITY_TYPE_STANDARD
+            if (!DisplayContent.alwaysCreateRootTask(tda.getWindowingMode(), mActivityType)
                     && mActivityType != ACTIVITY_TYPE_UNDEFINED) {
-                // For now there can be only one root task of a particular non-standard activity
-                // type on a display. So, get that ignoring whatever windowing mode it is
-                // currently in.
+                // Only Recents or Standard activity types are allowed to have more than one
+                // root task on a display, this is independent of whatever windowing mode it
+                // is currently in.
                 Task rootTask = tda.getRootTask(WINDOWING_MODE_UNDEFINED, mActivityType);
                 if (rootTask != null) {
                     throw new IllegalArgumentException("Root task=" + rootTask + " of activityType="
diff --git a/services/proguard.flags b/services/proguard.flags
index e11e613..261bb7c 100644
--- a/services/proguard.flags
+++ b/services/proguard.flags
@@ -47,6 +47,11 @@
 -keep,allowoptimization,allowaccessmodification class com.android.net.module.util.* { *; }
 -keep,allowoptimization,allowaccessmodification public class com.android.server.net.IpConfigStore { *; }
 -keep,allowoptimization,allowaccessmodification public class com.android.server.net.BaseNetworkObserver { *; }
+-keep,allowoptimization,allowaccessmodification class com.android.server.display.feature.DisplayManagerFlags { *; }
+-keep,allowoptimization,allowaccessmodification class android.app.admin.flags.FeatureFlagsImpl { *; }
+-keep,allowoptimization,allowaccessmodification class com.android.server.input.NativeInputManagerService$NativeImpl { *; }
+-keep,allowoptimization,allowaccessmodification class com.android.server.ThreadPriorityBooster { *; }
+-keep,allowaccessmodification class android.app.admin.flags.Flags { *; }
 
 # Referenced via CarServiceHelperService in car-frameworks-service (avoid removing)
 -keep public class com.android.server.utils.Slogf { *; }
@@ -99,9 +104,6 @@
 -keep,allowoptimization,allowaccessmodification class com.android.server.input.InputManagerService {
   <methods>;
 }
--keep,allowoptimization,allowaccessmodification class com.android.server.input.NativeInputManagerService$NativeImpl {
-  <methods>;
-}
 -keep,allowoptimization,allowaccessmodification class com.android.server.usb.UsbHostManager {
   *** usbDeviceRemoved(...);
   *** usbDeviceAdded(...);
diff --git a/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
index 47ae97f..7ceccc5 100644
--- a/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
@@ -44,6 +44,7 @@
 import android.app.IActivityManager;
 import android.app.IUidObserver;
 import android.app.usage.UsageStatsManager;
+import android.content.AttributionSourceState;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -229,12 +230,20 @@
     private AppStateTrackerTestable newInstance() throws Exception {
         MockitoAnnotations.initMocks(this);
 
-        when(mMockIAppOpsService.checkOperation(eq(TARGET_OP), anyInt(), anyString()))
-                .thenAnswer(inv -> {
-                    return mRestrictedPackages.indexOf(
-                            Pair.create(inv.getArgument(1), inv.getArgument(2))) >= 0 ?
-                            AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED;
-                });
+        when(mMockIAppOpsService.checkOperationWithState(eq(TARGET_OP), any()))
+                .thenAnswer(
+                        (Answer<Integer>)
+                                invocation -> {
+                                    AttributionSourceState attribution =
+                                            (AttributionSourceState) invocation.getArguments()[1];
+                                    return mRestrictedPackages.indexOf(
+                                                            Pair.create(
+                                                                    attribution.uid,
+                                                                    attribution.packageName))
+                                                    >= 0
+                                            ? AppOpsManager.MODE_IGNORED
+                                            : AppOpsManager.MODE_ALLOWED;
+                                });
 
         final AppStateTrackerTestable instance = new AppStateTrackerTestable();
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index 40b5458..3ee8050 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -69,6 +69,7 @@
 import android.app.IApplicationThread;
 import android.app.IUidObserver;
 import android.app.SyncNotedAppOp;
+import android.content.AttributionSourceState;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -223,12 +224,16 @@
         assertThat(sProcessListSettingsListener).isNotNull();
     }
 
-    private void mockNoteOperation() {
+    private void mockNoteOp() {
         SyncNotedAppOp allowed = new SyncNotedAppOp(AppOpsManager.MODE_ALLOWED,
                 AppOpsManager.OP_GET_USAGE_STATS, null, mContext.getPackageName());
-        when(mAppOpsService.noteOperation(eq(AppOpsManager.OP_GET_USAGE_STATS), eq(Process.myUid()),
-                nullable(String.class), nullable(String.class), any(Boolean.class),
-                nullable(String.class), any(Boolean.class))).thenReturn(allowed);
+        when(mAppOpsService.noteOperationWithState(
+                        eq(AppOpsManager.OP_GET_USAGE_STATS),
+                        any(AttributionSourceState.class),
+                        any(Boolean.class),
+                        nullable(String.class),
+                        any(Boolean.class)))
+                .thenReturn(allowed);
     }
 
     @After
@@ -609,7 +614,7 @@
      */
     @Test
     public void testDispatchUids_dispatchNeededChanges() throws RemoteException {
-        mockNoteOperation();
+        mockNoteOp();
 
         final int[] changesToObserve = {
             ActivityManager.UID_OBSERVER_PROCSTATE,
@@ -818,7 +823,7 @@
      */
     @Test
     public void testDispatchUidChanges_procStateCutpoint() throws RemoteException {
-        mockNoteOperation();
+        mockNoteOp();
 
         final IUidObserver observer = mock(IUidObserver.Stub.class);
 
@@ -888,7 +893,7 @@
      */
     @Test
     public void testDispatchUidChanges_validateUidsUpdated() {
-        mockNoteOperation();
+        mockNoteOp();
 
         final int[] changesForPendingItems = UID_RECORD_CHANGES;
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
index bb91939..dcbee83 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
@@ -113,6 +113,8 @@
 import android.app.NotificationManager;
 import android.app.role.RoleManager;
 import android.app.usage.AppStandbyInfo;
+import android.content.AttributionSource;
+import android.content.AttributionSourceState;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -2454,9 +2456,12 @@
             doReturn(granted ? MODE_ALLOWED : MODE_IGNORED)
                     .when(mAppOpsManager)
                     .checkOpNoThrow(op, uid, packageName);
+            AttributionSource attributionSource =
+                    new AttributionSource.Builder(uid).setPackageName(packageName).build();
+            AttributionSourceState attributionSourceState = attributionSource.asState();
             doReturn(granted ? MODE_ALLOWED : MODE_IGNORED)
                     .when(mIAppOpsService)
-                    .checkOperation(op, uid, packageName);
+                    .checkOperationWithState(eq(op), eq(attributionSourceState));
         } catch (RemoteException e) {
             // Ignore.
         }
diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
index 646f486..daed5df 100644
--- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
@@ -49,6 +49,7 @@
 import android.app.AppOpsManager;
 import android.app.AppOpsManager.OpEntry;
 import android.app.AppOpsManager.PackageOps;
+import android.content.AttributionSource;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.pm.PackageManagerInternal;
@@ -216,18 +217,21 @@
     }
 
     @Test
-    public void testNoteOperationAndGetOpsForPackage() {
+    public void testNoteOpAndGetOpsForPackage() {
         mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED);
         mAppOpsService.setMode(OP_WRITE_SMS, mMyUid, sMyPackageName, MODE_ERRORED);
+        AttributionSource attributionSource =
+                new AttributionSource.Builder(mMyUid).setPackageName(sMyPackageName).build();
 
         // Note an op that's allowed.
-        mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null, false, null, false);
+        mAppOpsService.noteOperationWithState(OP_READ_SMS, attributionSource.asState(), false,
+                null, false);
         List<PackageOps> loggedOps = getLoggedOps();
         assertContainsOp(loggedOps, OP_READ_SMS, mTestStartMillis, -1, MODE_ALLOWED);
 
         // Note another op that's not allowed.
-        mAppOpsService.noteOperation(OP_WRITE_SMS, mMyUid, sMyPackageName, null, false, null,
-                false);
+        mAppOpsService.noteOperationWithState(OP_WRITE_SMS, attributionSource.asState(), false,
+                null, false);
         loggedOps = getLoggedOps();
         assertContainsOp(loggedOps, OP_READ_SMS, mTestStartMillis, -1, MODE_ALLOWED);
         assertContainsOp(loggedOps, OP_WRITE_SMS, -1, mTestStartMillis, MODE_ERRORED);
@@ -239,20 +243,24 @@
      * ACCESS_COARSE_LOCATION op is used to check whether WIFI_SCAN is allowed.
      */
     @Test
-    public void testNoteOperationAndGetOpsForPackage_controlledByDifferentOp() {
+    public void testNoteOpAndGetOpsForPackage_controlledByDifferentOp() {
         // This op controls WIFI_SCAN
         mAppOpsService.setMode(OP_COARSE_LOCATION, mMyUid, sMyPackageName, MODE_ALLOWED);
 
-        assertThat(mAppOpsService.noteOperation(OP_WIFI_SCAN, mMyUid, sMyPackageName, null, false,
-                null, false).getOpMode()).isEqualTo(MODE_ALLOWED);
+        assertThat(mAppOpsService.noteOperationWithState(OP_WIFI_SCAN,
+                new AttributionSource.Builder(mMyUid).setPackageName(sMyPackageName)
+                        .build().asState(), false, null, false).getOpMode())
+                .isEqualTo(MODE_ALLOWED);
 
         assertContainsOp(getLoggedOps(), OP_WIFI_SCAN, mTestStartMillis, -1,
                 MODE_ALLOWED /* default for WIFI_SCAN; this is not changed or used in this test */);
 
         // Now set COARSE_LOCATION to ERRORED -> this will make WIFI_SCAN disabled as well.
         mAppOpsService.setMode(OP_COARSE_LOCATION, mMyUid, sMyPackageName, MODE_ERRORED);
-        assertThat(mAppOpsService.noteOperation(OP_WIFI_SCAN, mMyUid, sMyPackageName, null, false,
-                null, false).getOpMode()).isEqualTo(MODE_ERRORED);
+        assertThat(mAppOpsService.noteOperationWithState(OP_WIFI_SCAN,
+                new AttributionSource.Builder(mMyUid).setPackageName(sMyPackageName)
+                        .build().asState(), false, null, false)
+                .getOpMode()).isEqualTo(MODE_ERRORED);
 
         assertContainsOp(getLoggedOps(), OP_WIFI_SCAN, mTestStartMillis, mTestStartMillis,
                 MODE_ALLOWED /* default for WIFI_SCAN; this is not changed or used in this test */);
@@ -263,9 +271,12 @@
     public void testStatePersistence() {
         mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED);
         mAppOpsService.setMode(OP_WRITE_SMS, mMyUid, sMyPackageName, MODE_ERRORED);
-        mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null, false, null, false);
-        mAppOpsService.noteOperation(OP_WRITE_SMS, mMyUid, sMyPackageName, null, false, null,
-                false);
+        AttributionSource attributionSource =
+                new AttributionSource.Builder(mMyUid).setPackageName(sMyPackageName).build();
+        mAppOpsService.noteOperationWithState(OP_READ_SMS, attributionSource.asState(), false,
+                null, false);
+        mAppOpsService.noteOperationWithState(OP_WRITE_SMS, attributionSource.asState(), false,
+                null, false);
 
         mAppOpsService.shutdown();
 
@@ -283,7 +294,10 @@
     @Test
     public void testShutdown() {
         mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED);
-        mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null, false, null, false);
+        AttributionSource attributionSource =
+                new AttributionSource.Builder(mMyUid).setPackageName(sMyPackageName).build();
+        mAppOpsService.noteOperationWithState(OP_READ_SMS, attributionSource.asState(), false,
+                null, false);
         mAppOpsService.shutdown();
 
         // Create a new app ops service which will initialize its state from XML.
@@ -297,7 +311,10 @@
     @Test
     public void testGetOpsForPackage() {
         mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED);
-        mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null, false, null, false);
+        AttributionSource attributionSource =
+                new AttributionSource.Builder(mMyUid).setPackageName(sMyPackageName).build();
+        mAppOpsService.noteOperationWithState(OP_READ_SMS, attributionSource.asState(), false,
+                null, false);
 
         // Query all ops
         List<PackageOps> loggedOps = mAppOpsService.getOpsForPackage(
@@ -326,7 +343,10 @@
     @Test
     public void testPackageRemoved() {
         mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED);
-        mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null, false, null, false);
+        AttributionSource attributionSource =
+                new AttributionSource.Builder(mMyUid).setPackageName(sMyPackageName).build();
+        mAppOpsService.noteOperationWithState(OP_READ_SMS, attributionSource.asState(), false,
+                null, false);
 
         List<PackageOps> loggedOps = getLoggedOps();
         assertContainsOp(loggedOps, OP_READ_SMS, mTestStartMillis, -1, MODE_ALLOWED);
@@ -341,7 +361,8 @@
     @Test
     public void testPackageRemovedHistoricalOps() throws InterruptedException {
         mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED);
-        mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null, false, null, false);
+        mAppOpsService.noteOperationWithState(OP_READ_SMS, mMyUid, sMyPackageName, null, false,
+                null, false);
 
         AppOpsManager.HistoricalOps historicalOps = new AppOpsManager.HistoricalOps(0, 15000);
         historicalOps.increaseAccessCount(OP_READ_SMS, mMyUid, sMyPackageName, null,
@@ -381,7 +402,10 @@
     @Test
     public void testUidRemoved() {
         mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED);
-        mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null, false, null, false);
+        AttributionSource attributionSource =
+                new AttributionSource.Builder(mMyUid).setPackageName(sMyPackageName).build();
+        mAppOpsService.noteOperationWithState(OP_READ_SMS, attributionSource.asState(),
+                false, null, false);
 
         List<PackageOps> loggedOps = getLoggedOps();
         assertContainsOp(loggedOps, OP_READ_SMS, mTestStartMillis, -1, MODE_ALLOWED);
@@ -393,7 +417,10 @@
     @Test
     public void testUidStateInitializationDoesntClearState() throws InterruptedException {
         mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED);
-        mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null, false, null, false);
+        AttributionSource attributionSource =
+                new AttributionSource.Builder(mMyUid).setPackageName(sMyPackageName).build();
+        mAppOpsService.noteOperationWithState(OP_READ_SMS, attributionSource.asState(), false,
+                null, false);
         mAppOpsService.initializeUidStates();
         List<PackageOps> ops = mAppOpsService.getOpsForPackage(mMyUid, sMyPackageName,
                 new int[]{OP_READ_SMS});
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
index e7f1d16e..5c8a19c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java
@@ -182,6 +182,10 @@
                 any(LauncherActivityInfo.class), eq(mUserId), anyInt(), anyInt());
         doReturn(mIcon).when(mArchiveManager).decodeIcon(
                 any(ArchiveState.ArchiveActivityInfo.class));
+        Resources mockResources = mock(Resources.class);
+        doReturn(mockResources)
+                .when(mContext)
+                .getResources();
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
index ece3dfe..097cc51 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
@@ -17,12 +17,17 @@
 package com.android.server.media.projection;
 
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
 import static android.media.projection.MediaProjectionManager.TYPE_MIRRORING;
 import static android.media.projection.ReviewGrantedConsentResult.RECORD_CANCEL;
 import static android.media.projection.ReviewGrantedConsentResult.RECORD_CONTENT_DISPLAY;
 import static android.media.projection.ReviewGrantedConsentResult.RECORD_CONTENT_TASK;
 import static android.media.projection.ReviewGrantedConsentResult.UNKNOWN;
+import static android.view.ContentRecordingSession.TARGET_UID_FULL_SCREEN;
+import static android.view.ContentRecordingSession.TARGET_UID_UNKNOWN;
+import static android.view.ContentRecordingSession.createDisplaySession;
+import static android.view.ContentRecordingSession.createTaskSession;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
 
@@ -62,6 +67,7 @@
 import android.os.test.TestLooper;
 import android.platform.test.annotations.Presubmit;
 import android.view.ContentRecordingSession;
+import android.view.ContentRecordingSession.RecordContent;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.FlakyTest;
@@ -99,7 +105,7 @@
     private final ApplicationInfo mAppInfo = new ApplicationInfo();
     private final TestLooper mTestLooper = new TestLooper();
     private static final ContentRecordingSession DISPLAY_SESSION =
-            ContentRecordingSession.createDisplaySession(DEFAULT_DISPLAY);
+            createDisplaySession(DEFAULT_DISPLAY);
     // Callback registered by an app on a MediaProjection instance.
     private final FakeIMediaProjectionCallback mIMediaProjectionCallback =
             new FakeIMediaProjectionCallback();
@@ -142,7 +148,7 @@
     private MediaProjectionManagerService mService;
     private OffsettableClock mClock;
     private ContentRecordingSession mWaitingDisplaySession =
-            ContentRecordingSession.createDisplaySession(DEFAULT_DISPLAY);
+            createDisplaySession(DEFAULT_DISPLAY);
 
     @Mock
     private ActivityManagerInternal mAmInternal;
@@ -333,7 +339,7 @@
         projection.stop();
 
         verify(mMediaProjectionMetricsLogger)
-                .logStopped(UID, ContentRecordingSession.TARGET_UID_UNKNOWN);
+                .logStopped(UID, TARGET_UID_UNKNOWN);
     }
 
     @Test
@@ -351,7 +357,7 @@
         projection.stop();
 
         verify(mMediaProjectionMetricsLogger)
-                .logStopped(UID, ContentRecordingSession.TARGET_UID_FULL_SCREEN);
+                .logStopped(UID, TARGET_UID_FULL_SCREEN);
     }
 
     @Test
@@ -366,7 +372,7 @@
                 .when(mWindowManagerInternal)
                 .setContentRecordingSession(any(ContentRecordingSession.class));
         ContentRecordingSession taskSession =
-                ContentRecordingSession.createTaskSession(mock(IBinder.class), targetUid);
+                createTaskSession(mock(IBinder.class), targetUid);
         service.setContentRecordingSession(taskSession);
 
         projection.stop();
@@ -695,6 +701,26 @@
         verify(mMediaProjectionMetricsLogger).logAppSelectorDisplayed(hostUid);
     }
 
+    @Test
+    public void notifyWindowingModeChanged_forwardsToLogger() throws Exception {
+        int targetUid = 123;
+        mService =
+                new MediaProjectionManagerService(mContext, mMediaProjectionMetricsLoggerInjector);
+
+        ContentRecordingSession taskSession =
+                createTaskSession(mock(IBinder.class), targetUid);
+        mService.setContentRecordingSession(taskSession);
+
+        MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
+        projection.start(mIMediaProjectionCallback);
+
+        mService.notifyWindowingModeChanged(
+                RECORD_CONTENT_TASK, targetUid, WINDOWING_MODE_MULTI_WINDOW);
+
+        verify(mMediaProjectionMetricsLogger).logChangedWindowingMode(RECORD_CONTENT_TASK,
+                projection.uid, targetUid, WINDOWING_MODE_MULTI_WINDOW);
+    }
+
     /**
      * Executes and validates scenario where the consent result indicates the projection ends.
      */
@@ -755,7 +781,7 @@
      */
     private void testSetUserReviewGrantedConsentResult_startedSession(
             @ReviewGrantedConsentResult int consentResult,
-            @ContentRecordingSession.RecordContent int recordedContent)
+            @RecordContent int recordedContent)
             throws NameNotFoundException {
         MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
         projection.setLaunchCookie(mock(IBinder.class));
@@ -777,7 +803,7 @@
      */
     private void testSetUserReviewGrantedConsentResult_failedToStartSession(
             @ReviewGrantedConsentResult int consentResult,
-            @ContentRecordingSession.RecordContent int recordedContent)
+            @RecordContent int recordedContent)
             throws NameNotFoundException {
         MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
         projection.start(mIMediaProjectionCallback);
@@ -889,7 +915,7 @@
         int targetUid = 123455;
 
         ContentRecordingSession taskSession =
-                ContentRecordingSession.createTaskSession(mock(IBinder.class), targetUid);
+                createTaskSession(mock(IBinder.class), targetUid);
         service.setContentRecordingSession(taskSession);
 
         verify(mMediaProjectionMetricsLogger).logInProgress(projection.uid, targetUid);
@@ -970,7 +996,7 @@
         verify(mWatcherCallback, never()).onRecordingSessionSet(any(), any());
     }
 
-    private void verifySetSessionWithContent(@ContentRecordingSession.RecordContent int content) {
+    private void verifySetSessionWithContent(@RecordContent int content) {
         verify(mWindowManagerInternal, atLeastOnce()).setContentRecordingSession(
                 mSessionCaptor.capture());
         assertThat(mSessionCaptor.getValue()).isNotNull();
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionMetricsLoggerTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionMetricsLoggerTest.java
index ad1cd6e..72ce9fe 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionMetricsLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionMetricsLoggerTest.java
@@ -16,6 +16,14 @@
 
 package com.android.server.media.projection;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+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.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.view.ContentRecordingSession.RECORD_CONTENT_DISPLAY;
+import static android.view.ContentRecordingSession.RECORD_CONTENT_TASK;
+
 import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN;
 import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_APP_SELECTOR_DISPLAYED;
 import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_CANCELLED;
@@ -24,6 +32,13 @@
 import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_PERMISSION_REQUEST_DISPLAYED;
 import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_STOPPED;
 import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__STATE__MEDIA_PROJECTION_STATE_UNKNOWN;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_APP_TASK;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_DISPLAY;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_UNKNOWN;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_FREEFORM;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_FULLSCREEN;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_SPLIT_SCREEN;
+import static com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_UNKNOWN;
 
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
@@ -38,10 +53,14 @@
 
 import com.android.internal.util.FrameworkStatsLog;
 
+import com.google.common.truth.Expect;
+
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 import java.time.Duration;
@@ -60,12 +79,18 @@
     private static final int TEST_TARGET_UID = 456;
     private static final int TEST_CREATION_SOURCE = 789;
 
+    private static final int TEST_WINDOWING_MODE = 987;
+    private static final int TEST_CONTENT_TO_RECORD = 654;
+
     @Mock private FrameworkStatsLogWrapper mFrameworkStatsLogWrapper;
     @Mock private MediaProjectionSessionIdGenerator mSessionIdGenerator;
     @Mock private MediaProjectionTimestampStore mTimestampStore;
 
     private MediaProjectionMetricsLogger mLogger;
 
+    @Rule
+    public Expect mExpect = Expect.create();
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
@@ -93,7 +118,7 @@
     public void logInitiated_logsHostUid() {
         mLogger.logInitiated(TEST_HOST_UID, TEST_CREATION_SOURCE);
 
-        verifyHostUidLogged(TEST_HOST_UID);
+        verifyStateChangedHostUidLogged(TEST_HOST_UID);
     }
 
     @Test
@@ -107,7 +132,7 @@
     public void logInitiated_logsUnknownTargetUid() {
         mLogger.logInitiated(TEST_HOST_UID, TEST_CREATION_SOURCE);
 
-        verifyTargetUidLogged(-2);
+        verifyStageChangedTargetUidLogged(-2);
     }
 
     @Test
@@ -178,14 +203,14 @@
     public void logStopped_logsHostUid() {
         mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID);
 
-        verifyHostUidLogged(TEST_HOST_UID);
+        verifyStateChangedHostUidLogged(TEST_HOST_UID);
     }
 
     @Test
     public void logStopped_logsTargetUid() {
         mLogger.logStopped(TEST_HOST_UID, TEST_TARGET_UID);
 
-        verifyTargetUidLogged(TEST_TARGET_UID);
+        verifyStageChangedTargetUidLogged(TEST_TARGET_UID);
     }
 
     @Test
@@ -263,14 +288,14 @@
     public void logInProgress_logsHostUid() {
         mLogger.logInProgress(TEST_HOST_UID, TEST_TARGET_UID);
 
-        verifyHostUidLogged(TEST_HOST_UID);
+        verifyStateChangedHostUidLogged(TEST_HOST_UID);
     }
 
     @Test
     public void logInProgress_logsTargetUid() {
         mLogger.logInProgress(TEST_HOST_UID, TEST_TARGET_UID);
 
-        verifyTargetUidLogged(TEST_TARGET_UID);
+        verifyStageChangedTargetUidLogged(TEST_TARGET_UID);
     }
 
     @Test
@@ -336,14 +361,14 @@
     public void logPermissionRequestDisplayed_logsHostUid() {
         mLogger.logPermissionRequestDisplayed(TEST_HOST_UID);
 
-        verifyHostUidLogged(TEST_HOST_UID);
+        verifyStateChangedHostUidLogged(TEST_HOST_UID);
     }
 
     @Test
     public void logPermissionRequestDisplayed_logsUnknownTargetUid() {
         mLogger.logPermissionRequestDisplayed(TEST_HOST_UID);
 
-        verifyTargetUidLogged(-2);
+        verifyStageChangedTargetUidLogged(-2);
     }
 
     @Test
@@ -409,14 +434,14 @@
     public void logAppSelectorDisplayed_logsHostUid() {
         mLogger.logAppSelectorDisplayed(TEST_HOST_UID);
 
-        verifyHostUidLogged(TEST_HOST_UID);
+        verifyStateChangedHostUidLogged(TEST_HOST_UID);
     }
 
     @Test
     public void logAppSelectorDisplayed_logsUnknownTargetUid() {
         mLogger.logAppSelectorDisplayed(TEST_HOST_UID);
 
-        verifyTargetUidLogged(-2);
+        verifyStageChangedTargetUidLogged(-2);
     }
 
     @Test
@@ -492,14 +517,14 @@
     public void logProjectionPermissionRequestCancelled_logsHostUid() {
         mLogger.logProjectionPermissionRequestCancelled(TEST_HOST_UID);
 
-        verifyHostUidLogged(TEST_HOST_UID);
+        verifyStateChangedHostUidLogged(TEST_HOST_UID);
     }
 
     @Test
     public void logProjectionPermissionRequestCancelled_logsUnknownTargetUid() {
         mLogger.logProjectionPermissionRequestCancelled(TEST_HOST_UID);
 
-        verifyTargetUidLogged(-2);
+        verifyStageChangedTargetUidLogged(-2);
     }
 
     @Test
@@ -510,9 +535,88 @@
                 MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_UNKNOWN);
     }
 
+    @Test
+    public void logWindowingModeChanged_logsTargetChangedAtomId() {
+        mLogger.logChangedWindowingMode(
+                TEST_CONTENT_TO_RECORD, TEST_HOST_UID, TEST_TARGET_UID, TEST_WINDOWING_MODE);
+
+        verifyTargetChangedAtomIdLogged();
+    }
+
+    @Test
+    public void logWindowingModeChanged_logsTargetType() {
+        MediaProjectionMetricsLogger logger = Mockito.spy(mLogger);
+        final int testTargetType = 111;
+        when(logger.contentToRecordToTargetType(TEST_CONTENT_TO_RECORD)).thenReturn(testTargetType);
+        logger.logChangedWindowingMode(
+                TEST_CONTENT_TO_RECORD, TEST_HOST_UID, TEST_TARGET_UID, TEST_WINDOWING_MODE);
+        verifyTargetTypeLogged(testTargetType);
+    }
+
+    @Test
+    public void logWindowingModeChanged_logsHostUid() {
+        mLogger.logChangedWindowingMode(
+                TEST_CONTENT_TO_RECORD, TEST_HOST_UID, TEST_TARGET_UID, TEST_WINDOWING_MODE);
+        verifyTargetChangedHostUidLogged(TEST_HOST_UID);
+    }
+
+    @Test
+    public void logWindowingModeChanged_logsTargetUid() {
+        mLogger.logChangedWindowingMode(
+                TEST_CONTENT_TO_RECORD, TEST_HOST_UID, TEST_TARGET_UID, TEST_WINDOWING_MODE);
+        verifyTargetChangedTargetUidLogged(TEST_TARGET_UID);
+    }
+
+    @Test
+    public void logWindowingModeChanged_logsTargetWindowingMode() {
+        MediaProjectionMetricsLogger logger = Mockito.spy(mLogger);
+        final int testTargetWindowingMode = 222;
+        when(logger.windowingModeToTargetWindowingMode(TEST_WINDOWING_MODE))
+                .thenReturn(testTargetWindowingMode);
+        logger.logChangedWindowingMode(
+                TEST_CONTENT_TO_RECORD, TEST_HOST_UID, TEST_TARGET_UID, TEST_WINDOWING_MODE);
+        verifyWindowingModeLogged(testTargetWindowingMode);
+    }
+
+    @Test
+    public void testContentToRecordToTargetType() {
+        mExpect.that(mLogger.contentToRecordToTargetType(RECORD_CONTENT_DISPLAY))
+                .isEqualTo(MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_DISPLAY);
+
+        mExpect.that(mLogger.contentToRecordToTargetType(RECORD_CONTENT_TASK))
+                .isEqualTo(MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_APP_TASK);
+
+        mExpect.that(mLogger.contentToRecordToTargetType(2))
+                .isEqualTo(MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_UNKNOWN);
+
+        mExpect.that(mLogger.contentToRecordToTargetType(-1))
+                .isEqualTo(MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_UNKNOWN);
+
+        mExpect.that(mLogger.contentToRecordToTargetType(100))
+                .isEqualTo(MEDIA_PROJECTION_TARGET_CHANGED__TARGET_TYPE__TARGET_TYPE_UNKNOWN);
+    }
+
+    @Test
+    public void testWindowingModeToTargetWindowingMode() {
+        mExpect.that(mLogger.windowingModeToTargetWindowingMode(WINDOWING_MODE_FULLSCREEN))
+                .isEqualTo(MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_FULLSCREEN);
+
+        mExpect.that(mLogger.windowingModeToTargetWindowingMode(WINDOWING_MODE_MULTI_WINDOW))
+                .isEqualTo(MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_SPLIT_SCREEN);
+
+        mExpect.that(mLogger.windowingModeToTargetWindowingMode(WINDOWING_MODE_FREEFORM))
+                .isEqualTo(MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_FREEFORM);
+
+        mExpect.that(mLogger.windowingModeToTargetWindowingMode(WINDOWING_MODE_PINNED))
+                .isEqualTo(MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_UNKNOWN);
+
+        mExpect.that(mLogger.windowingModeToTargetWindowingMode(WINDOWING_MODE_UNDEFINED))
+                .isEqualTo(MEDIA_PROJECTION_TARGET_CHANGED__TARGET_WINDOWING_MODE__WINDOWING_MODE_UNKNOWN);
+    }
+
     private void verifyStateChangedAtomIdLogged() {
         verify(mFrameworkStatsLogWrapper)
-                .write(
+                .writeStateChanged(
                         /* code= */ eq(FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED),
                         /* sessionId= */ anyInt(),
                         /* state= */ anyInt(),
@@ -525,7 +629,7 @@
 
     private void verifyStateLogged(int state) {
         verify(mFrameworkStatsLogWrapper)
-                .write(
+                .writeStateChanged(
                         /* code= */ anyInt(),
                         /* sessionId= */ anyInt(),
                         eq(state),
@@ -536,9 +640,9 @@
                         /* creationSource= */ anyInt());
     }
 
-    private void verifyHostUidLogged(int hostUid) {
+    private void verifyStateChangedHostUidLogged(int hostUid) {
         verify(mFrameworkStatsLogWrapper)
-                .write(
+                .writeStateChanged(
                         /* code= */ anyInt(),
                         /* sessionId= */ anyInt(),
                         /* state= */ anyInt(),
@@ -551,7 +655,7 @@
 
     private void verifyCreationSourceLogged(int creationSource) {
         verify(mFrameworkStatsLogWrapper)
-                .write(
+                .writeStateChanged(
                         /* code= */ anyInt(),
                         /* sessionId= */ anyInt(),
                         /* state= */ anyInt(),
@@ -562,9 +666,9 @@
                         eq(creationSource));
     }
 
-    private void verifyTargetUidLogged(int targetUid) {
+    private void verifyStageChangedTargetUidLogged(int targetUid) {
         verify(mFrameworkStatsLogWrapper)
-                .write(
+                .writeStateChanged(
                         /* code= */ anyInt(),
                         /* sessionId= */ anyInt(),
                         /* state= */ anyInt(),
@@ -577,7 +681,7 @@
 
     private void verifyTimeSinceLastActiveSessionLogged(int timeSinceLastActiveSession) {
         verify(mFrameworkStatsLogWrapper)
-                .write(
+                .writeStateChanged(
                         /* code= */ anyInt(),
                         /* sessionId= */ anyInt(),
                         /* state= */ anyInt(),
@@ -590,7 +694,7 @@
 
     private void verifySessionIdLogged(int newSessionId) {
         verify(mFrameworkStatsLogWrapper)
-                .write(
+                .writeStateChanged(
                         /* code= */ anyInt(),
                         /* sessionId= */ eq(newSessionId),
                         /* state= */ anyInt(),
@@ -603,7 +707,7 @@
 
     private void verifyPreviousStateLogged(int previousState) {
         verify(mFrameworkStatsLogWrapper)
-                .write(
+                .writeStateChanged(
                         /* code= */ anyInt(),
                         /* sessionId= */ anyInt(),
                         /* state= */ anyInt(),
@@ -613,4 +717,59 @@
                         /* timeSinceLastActive= */ anyInt(),
                         /* creationSource= */ anyInt());
     }
+
+    private void verifyTargetChangedAtomIdLogged() {
+        verify(mFrameworkStatsLogWrapper)
+                .writeTargetChanged(
+                        eq(FrameworkStatsLog.MEDIA_PROJECTION_TARGET_CHANGED),
+                        /* sessionId= */ anyInt(),
+                        /* targetType= */ anyInt(),
+                        /* hostUid= */ anyInt(),
+                        /* targetUid= */ anyInt(),
+                        /* targetWindowingMode= */ anyInt());
+    }
+
+    private void verifyTargetTypeLogged(int targetType) {
+        verify(mFrameworkStatsLogWrapper)
+                .writeTargetChanged(
+                        /* code= */ anyInt(),
+                        /* sessionId= */ anyInt(),
+                        eq(targetType),
+                        /* hostUid= */ anyInt(),
+                        /* targetUid= */ anyInt(),
+                        /* targetWindowingMode= */ anyInt());
+    }
+
+    private void verifyTargetChangedHostUidLogged(int hostUid) {
+        verify(mFrameworkStatsLogWrapper)
+                .writeTargetChanged(
+                        /* code= */ anyInt(),
+                        /* sessionId= */ anyInt(),
+                        /* targetType= */ anyInt(),
+                        eq(hostUid),
+                        /* targetUid= */ anyInt(),
+                        /* targetWindowingMode= */ anyInt());
+    }
+
+    private void verifyTargetChangedTargetUidLogged(int targetUid) {
+        verify(mFrameworkStatsLogWrapper)
+                .writeTargetChanged(
+                        /* code= */ anyInt(),
+                        /* sessionId= */ anyInt(),
+                        /* targetType= */ anyInt(),
+                        /* hostUid= */ anyInt(),
+                        eq(targetUid),
+                        /* targetWindowingMode= */ anyInt());
+    }
+
+    private void verifyWindowingModeLogged(int targetWindowingMode) {
+        verify(mFrameworkStatsLogWrapper)
+                .writeTargetChanged(
+                        /* code= */ anyInt(),
+                        /* sessionId= */ anyInt(),
+                        /* targetType= */ anyInt(),
+                        /* hostUid= */ anyInt(),
+                        /* targetUid= */ anyInt(),
+                        eq(targetWindowingMode));
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java b/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java
index 398148f..01a91c1 100644
--- a/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java
@@ -30,6 +30,7 @@
 import static org.junit.Assert.fail;
 
 import android.app.AppGlobals;
+import android.content.AttributionSource;
 import android.content.Context;
 import android.content.pm.IPackageManager;
 import android.content.pm.LauncherApps;
@@ -281,12 +282,16 @@
         };
         iAppOps.startWatchingMode(code, TEST_APP_PACKAGE_NAME, watcher);
         final int testPackageUid = mPackageManager.getPackageUid(TEST_APP_PACKAGE_NAME, 0);
-        int opMode = iAppOps.checkOperation(code, testPackageUid, TEST_APP_PACKAGE_NAME);
+        AttributionSource attributionSource =
+                new AttributionSource.Builder(testPackageUid)
+                        .setPackageName(TEST_APP_PACKAGE_NAME)
+                        .build();
+        int opMode = iAppOps.checkOperationWithState(code, attributionSource.asState());
         assertEquals("Op " + opToName(code) + " disallowed for unsuspended package", MODE_ALLOWED,
                 opMode);
         suspendTestPackage(null, null, null);
         assertTrue("AppOpsWatcher did not callback", latch.await(5, TimeUnit.SECONDS));
-        opMode = iAppOps.checkOperation(code, testPackageUid, TEST_APP_PACKAGE_NAME);
+        opMode = iAppOps.checkOperationWithState(code, attributionSource.asState());
         assertEquals("Op " + opToName(code) + " allowed for suspended package", MODE_IGNORED,
                 opMode);
         iAppOps.stopWatchingMode(watcher);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
index 78566fb..887e5ee 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
@@ -42,6 +42,7 @@
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
 
 import android.app.WindowConfiguration;
 import android.content.pm.ActivityInfo;
@@ -232,7 +233,7 @@
     @Test
     public void testOnConfigurationChanged_neverRecording() {
         defaultInit();
-        mContentRecorder.onConfigurationChanged(ORIENTATION_PORTRAIT);
+        mContentRecorder.onConfigurationChanged(ORIENTATION_PORTRAIT, WINDOWING_MODE_FULLSCREEN);
 
         verify(mTransaction, never()).setPosition(eq(mRecordedSurface), anyFloat(), anyFloat());
         verify(mTransaction, never()).setMatrix(eq(mRecordedSurface), anyFloat(), anyFloat(),
@@ -248,7 +249,7 @@
         @Configuration.Orientation final int lastOrientation =
                 mDisplayContent.getConfiguration().orientation == ORIENTATION_PORTRAIT
                         ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT;
-        mContentRecorder.onConfigurationChanged(lastOrientation);
+        mContentRecorder.onConfigurationChanged(lastOrientation, WINDOWING_MODE_FULLSCREEN);
 
         verify(mTransaction, atLeast(2)).setPosition(eq(mRecordedSurface), anyFloat(),
                 anyFloat());
@@ -266,7 +267,8 @@
         // The user rotates the device, so the host app resizes the virtual display for the capture.
         resizeDisplay(mDisplayContent, newWidth, mSurfaceSize.y);
         resizeDisplay(mVirtualDisplayContent, newWidth, mSurfaceSize.y);
-        mContentRecorder.onConfigurationChanged(mDisplayContent.getConfiguration().orientation);
+        mContentRecorder.onConfigurationChanged(
+                mDisplayContent.getConfiguration().orientation, WINDOWING_MODE_FULLSCREEN);
 
         verify(mTransaction, atLeast(2)).setPosition(eq(mRecordedSurface), anyFloat(),
                 anyFloat());
@@ -283,7 +285,7 @@
         // Change a value that we shouldn't rely upon; it has the wrong type.
         mVirtualDisplayContent.setOverrideOrientation(SCREEN_ORIENTATION_FULL_SENSOR);
         mContentRecorder.onConfigurationChanged(
-                mVirtualDisplayContent.getConfiguration().orientation);
+                mVirtualDisplayContent.getConfiguration().orientation, WINDOWING_MODE_FULLSCREEN);
 
         // No resize is issued, only the initial transformations when we started recording.
         verify(mTransaction).setPosition(eq(mRecordedSurface), anyFloat(),
@@ -307,7 +309,7 @@
         doReturn(newSurfaceSize).when(mWm.mDisplayManagerInternal).getDisplaySurfaceDefaultSize(
                 anyInt());
         mContentRecorder.onConfigurationChanged(
-                mVirtualDisplayContent.getConfiguration().orientation);
+                mVirtualDisplayContent.getConfiguration().orientation, WINDOWING_MODE_FULLSCREEN);
 
         // No resize is issued, only the initial transformations when we started recording.
         verify(mTransaction, atLeast(2)).setPosition(eq(mRecordedSurface), anyFloat(),
@@ -379,6 +381,55 @@
     }
 
     @Test
+    public void testTaskWindowingModeChanged_changeWindowMode_notifyWindowModeChanged() {
+        defaultInit();
+        // WHEN a recording is ongoing.
+        mTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        mContentRecorder.setContentRecordingSession(mTaskSession);
+        mContentRecorder.updateRecording();
+        assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
+
+        // THEN the windowing mode change callback is notified.
+        verify(mMediaProjectionManagerWrapper)
+                .notifyWindowingModeChanged(mTaskSession.getContentToRecord(),
+                        mTaskSession.getTargetUid(), WINDOWING_MODE_FULLSCREEN);
+
+        // WHEN a configuration change arrives, and the task is now multi-window mode.
+        mTask.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+        Configuration configuration = mTask.getConfiguration();
+        mTask.onConfigurationChanged(configuration);
+
+        // THEN windowing mode change callback is notified again.
+        verify(mMediaProjectionManagerWrapper)
+                .notifyWindowingModeChanged(mTaskSession.getContentToRecord(),
+                mTaskSession.getTargetUid(), WINDOWING_MODE_MULTI_WINDOW);
+    }
+
+    @Test
+    public void testTaskWindowingModeChanged_sameWindowMode_notifyWindowModeChanged() {
+        defaultInit();
+        // WHEN a recording is ongoing.
+        mTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        mContentRecorder.setContentRecordingSession(mTaskSession);
+        mContentRecorder.updateRecording();
+        assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
+
+        // THEN the windowing mode change callback is notified.
+        verify(mMediaProjectionManagerWrapper)
+                .notifyWindowingModeChanged(mTaskSession.getContentToRecord(),
+                        mTaskSession.getTargetUid(), WINDOWING_MODE_FULLSCREEN);
+
+        // WHEN a configuration change arrives, and the task is STILL fullscreen.
+        mTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        Configuration configuration = mTask.getConfiguration();
+        mTask.onConfigurationChanged(configuration);
+
+        // THEN the windowing mode change callback is NOT called notified again.
+        verify(mMediaProjectionManagerWrapper, times(1))
+                .notifyWindowingModeChanged(anyInt(), anyInt(), anyInt());
+    }
+
+    @Test
     public void testTaskWindowingModeChanged_pip_stopsRecording() {
         defaultInit();
         // WHEN a recording is ongoing.
@@ -421,9 +472,12 @@
         mContentRecorder.updateRecording();
         assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
 
-        // THEN the visibility change callback is notified.
+        // THEN the visibility change & windowing mode change callbacks are notified.
         verify(mMediaProjectionManagerWrapper)
                 .notifyActiveProjectionCapturedContentVisibilityChanged(true);
+        verify(mMediaProjectionManagerWrapper)
+                .notifyWindowingModeChanged(mTaskSession.getContentToRecord(),
+                        mTaskSession.getTargetUid(), mRootWindowContainer.getWindowingMode());
     }
 
     @Test
@@ -434,9 +488,12 @@
         mContentRecorder.updateRecording();
         assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
 
-        // THEN the visibility change callback is notified.
+        // THEN the visibility change & windowing mode change callbacks are notified.
         verify(mMediaProjectionManagerWrapper)
                 .notifyActiveProjectionCapturedContentVisibilityChanged(true);
+        verify(mMediaProjectionManagerWrapper)
+                .notifyWindowingModeChanged(mDisplaySession.getContentToRecord(),
+                        mDisplaySession.getTargetUid(), mRootWindowContainer.getWindowingMode());
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/InputMethodDialogWindowContextTest.java b/services/tests/wmtests/src/com/android/server/wm/InputMethodDialogWindowContextTest.java
index 4165911..da3a02a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InputMethodDialogWindowContextTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InputMethodDialogWindowContextTest.java
@@ -160,6 +160,9 @@
         final DisplayAreaGroup firstDaGroup = mSecondaryDisplay.mFirstRoot;
         maxBoundsVerifier.setMaxBounds(firstDaGroup.getMaxBounds());
 
+        // Clear the previous invocation histories in case we may count the previous
+        // onConfigurationChanged invocation into the next verification.
+        clearInvocations(tokenClient, imeContainer);
         firstDaGroup.placeImeContainer(imeContainer);
 
         verify(imeContainer, timeout(WAIT_TIMEOUT_MS)).onConfigurationChanged(
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 1f32c97..55fecfc 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -19,6 +19,7 @@
 
 import android.Manifest;
 import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -54,6 +55,7 @@
 import com.android.internal.telecom.ClientTransactionalServiceRepository;
 import com.android.internal.telecom.ClientTransactionalServiceWrapper;
 import com.android.internal.telecom.ITelecomService;
+import com.android.server.telecom.flags.Flags;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -412,6 +414,14 @@
             "android.telecom.extra.CALL_CREATED_TIME_MILLIS";
 
     /**
+     * The extra for call log uri that was used to mark missed calls as read when dialer gets the
+     * notification on reboot.
+     */
+    @FlaggedApi(Flags.FLAG_ADD_CALL_URI_FOR_MISSED_CALLS)
+    public static final String EXTRA_CALL_LOG_URI =
+            "android.telecom.extra.CALL_LOG_URI";
+
+    /**
      * Optional extra for incoming containing a long which specifies the time the
      * call was answered by user. This value is in milliseconds.
      * @hide
@@ -2361,6 +2371,11 @@
      * <p>
      * <b>Note</b>: {@link android.app.Notification.CallStyle} notifications should be posted after
      * the call is placed in order for the notification to be non-dismissible.
+     * <p><b>Note</b>: Call Forwarding MMI codes can only be dialed by applications that are
+     * configured as the user defined default dialer or system dialer role. If a call containing a
+     * call forwarding MMI code is placed by an application that is not in one of these roles, the
+     * dialer will be launched with a UI showing the MMI code already populated so that the user can
+     * confirm the action before the call is placed.
      * @param address The address to make the call to.
      * @param extras Bundle of extras to use with the call.
      */
diff --git a/telephony/java/android/telephony/IBooleanConsumer.aidl b/telephony/java/android/telephony/IBooleanConsumer.aidl
new file mode 100644
index 0000000..69f7c9e
--- /dev/null
+++ b/telephony/java/android/telephony/IBooleanConsumer.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+/**
+ * Consumer pattern for an operation that requires a boolean result from another process to finish.
+ * @hide
+ */
+oneway interface IBooleanConsumer {
+    void accept(boolean result);
+}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/IIntegerConsumer.aidl b/telephony/java/android/telephony/IIntegerConsumer.aidl
new file mode 100644
index 0000000..3e84c32
--- /dev/null
+++ b/telephony/java/android/telephony/IIntegerConsumer.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+/**
+ * Consumer pattern for an operation that requires an integer result from another process to finish.
+ * @hide
+ */
+oneway interface IIntegerConsumer {
+    void accept(int result);
+}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 8e90fe7..f8608b8 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -265,8 +265,8 @@
         }
     }
 
-    private static VoidPropertyInvalidatedCache<Integer> sGetDefaultSubIdCache =
-            new VoidPropertyInvalidatedCache<>(ISub::getDefaultSubId,
+    private static IntegerPropertyInvalidatedCache<Integer> sGetDefaultSubIdCacheAsUser =
+            new IntegerPropertyInvalidatedCache<>(ISub::getDefaultSubIdAsUser,
                     CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY,
                     INVALID_SUBSCRIPTION_ID);
 
@@ -275,8 +275,8 @@
                     CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY,
                     INVALID_SUBSCRIPTION_ID);
 
-    private static VoidPropertyInvalidatedCache<Integer> sGetDefaultSmsSubIdCache =
-            new VoidPropertyInvalidatedCache<>(ISub::getDefaultSmsSubId,
+    private static IntegerPropertyInvalidatedCache<Integer> sGetDefaultSmsSubIdCacheAsUser =
+            new IntegerPropertyInvalidatedCache<>(ISub::getDefaultSmsSubIdAsUser,
                     CACHE_KEY_SUBSCRIPTION_MANAGER_SERVICE_PROPERTY,
                     INVALID_SUBSCRIPTION_ID);
 
@@ -2309,7 +2309,7 @@
      * @return the "system" default subscription id.
      */
     public static int getDefaultSubscriptionId() {
-        return sGetDefaultSubIdCache.query(null);
+        return sGetDefaultSubIdCacheAsUser.query(Process.myUserHandle().getIdentifier());
     }
 
     /**
@@ -2325,7 +2325,7 @@
         try {
             ISub iSub = TelephonyManager.getSubscriptionService();
             if (iSub != null) {
-                subId = iSub.getDefaultVoiceSubId();
+                subId = iSub.getDefaultVoiceSubIdAsUser(Process.myUserHandle().getIdentifier());
             }
         } catch (RemoteException ex) {
             // ignore it
@@ -2397,7 +2397,7 @@
      * @return the default SMS subscription Id.
      */
     public static int getDefaultSmsSubscriptionId() {
-        return sGetDefaultSmsSubIdCache.query(null);
+        return sGetDefaultSmsSubIdCacheAsUser.query(Process.myUserHandle().getIdentifier());
     }
 
     /**
@@ -3927,10 +3927,10 @@
      * @hide
      */
     public static void disableCaching() {
-        sGetDefaultSubIdCache.disableLocal();
+        sGetDefaultSubIdCacheAsUser.disableLocal();
         sGetDefaultDataSubIdCache.disableLocal();
         sGetActiveDataSubscriptionIdCache.disableLocal();
-        sGetDefaultSmsSubIdCache.disableLocal();
+        sGetDefaultSmsSubIdCacheAsUser.disableLocal();
         sGetSlotIndexCache.disableLocal();
         sGetSubIdCache.disableLocal();
         sGetPhoneIdCache.disableLocal();
@@ -3941,10 +3941,10 @@
      *
      * @hide */
     public static void clearCaches() {
-        sGetDefaultSubIdCache.clear();
+        sGetDefaultSubIdCacheAsUser.clear();
         sGetDefaultDataSubIdCache.clear();
         sGetActiveDataSubscriptionIdCache.clear();
-        sGetDefaultSmsSubIdCache.clear();
+        sGetDefaultSmsSubIdCacheAsUser.clear();
         sGetSlotIndexCache.clear();
         sGetSubIdCache.clear();
         sGetPhoneIdCache.clear();
diff --git a/telephony/java/android/telephony/satellite/stub/ISatellite.aidl b/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
index e2cd4f8..711be02 100644
--- a/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
+++ b/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
@@ -16,14 +16,14 @@
 
 package android.telephony.satellite.stub;
 
+import android.telephony.IBooleanConsumer;
+import android.telephony.IIntegerConsumer;
+
 import android.telephony.satellite.stub.INtnSignalStrengthConsumer;
 import android.telephony.satellite.stub.ISatelliteCapabilitiesConsumer;
 import android.telephony.satellite.stub.ISatelliteListener;
 import android.telephony.satellite.stub.SatelliteDatagram;
 
-import com.android.internal.telephony.IBooleanConsumer;
-import com.android.internal.telephony.IIntegerConsumer;
-
 /**
  * {@hide}
  */
diff --git a/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java b/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
index a636a61..c0d0830 100644
--- a/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
+++ b/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
@@ -19,10 +19,10 @@
 import android.annotation.NonNull;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.telephony.IBooleanConsumer;
+import android.telephony.IIntegerConsumer;
 import android.util.Log;
 
-import com.android.internal.telephony.IBooleanConsumer;
-import com.android.internal.telephony.IIntegerConsumer;
 import com.android.internal.telephony.util.TelephonyUtils;
 
 import java.util.List;
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index a5a23e8..d2dbeb7 100644
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -239,6 +239,7 @@
     int getSubId(int slotIndex);
 
     int getDefaultSubId();
+    int getDefaultSubIdAsUser(int userId);
 
     int getPhoneId(int subId);
 
@@ -251,10 +252,12 @@
     void setDefaultDataSubId(int subId);
 
     int getDefaultVoiceSubId();
+    int getDefaultVoiceSubIdAsUser(int userId);
 
     void setDefaultVoiceSubId(int subId);
 
     int getDefaultSmsSubId();
+    int getDefaultSmsSubIdAsUser(int userId);
 
     void setDefaultSmsSubId(int subId);
 
diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp
index f4f2be6..3d49d81 100644
--- a/tests/FlickerTests/Android.bp
+++ b/tests/FlickerTests/Android.bp
@@ -166,22 +166,6 @@
 }
 
 android_test {
-    name: "FlickerTestsAppLaunch",
-    defaults: ["FlickerTestsDefault"],
-    additional_manifests: ["manifests/AndroidManifestAppLaunch.xml"],
-    package_name: "com.android.server.wm.flicker.launch",
-    instrumentation_target_package: "com.android.server.wm.flicker.launch",
-    srcs: [
-        ":FlickerTestsBase-src",
-        ":FlickerTestsAppLaunchCommon-src",
-        ":FlickerTestsAppLaunch2-src",
-    ],
-    exclude_srcs: [
-        ":FlickerTestsActivityEmbedding-src",
-    ],
-}
-
-android_test {
     name: "FlickerTestsAppLaunch1",
     defaults: ["FlickerTestsDefault"],
     additional_manifests: ["manifests/AndroidManifestAppLaunch.xml"],
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingPlaceholderSplitTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingPlaceholderSplitTest.kt
index 48edf6d..59ff0c6 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingPlaceholderSplitTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingPlaceholderSplitTest.kt
@@ -88,14 +88,20 @@
         flicker.assertWm {
             notContains(ActivityEmbeddingAppHelper.PLACEHOLDER_PRIMARY_COMPONENT)
                 .then()
-                .isAppWindowInvisible(ActivityEmbeddingAppHelper.PLACEHOLDER_PRIMARY_COMPONENT)
+                .isAppWindowInvisible(
+                    ActivityEmbeddingAppHelper.PLACEHOLDER_PRIMARY_COMPONENT,
+                    isOptional = true
+                )
                 .then()
                 .isAppWindowVisible(ActivityEmbeddingAppHelper.PLACEHOLDER_PRIMARY_COMPONENT)
         }
         flicker.assertWm {
             notContains(ActivityEmbeddingAppHelper.PLACEHOLDER_SECONDARY_COMPONENT)
                 .then()
-                .isAppWindowInvisible(ActivityEmbeddingAppHelper.PLACEHOLDER_SECONDARY_COMPONENT)
+                .isAppWindowInvisible(
+                    ActivityEmbeddingAppHelper.PLACEHOLDER_SECONDARY_COMPONENT,
+                    isOptional = true
+                )
                 .then()
                 .isAppWindowVisible(ActivityEmbeddingAppHelper.PLACEHOLDER_SECONDARY_COMPONENT)
         }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenTransferSplashscreenAppFromLauncherTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenTransferSplashscreenAppFromLauncherTransition.kt
index 2e9620b..17f7490 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenTransferSplashscreenAppFromLauncherTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/common/OpenTransferSplashscreenAppFromLauncherTransition.kt
@@ -24,6 +24,7 @@
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.helpers.TransferSplashscreenAppHelper
+import com.android.server.wm.flicker.replacesLayer
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -119,7 +120,13 @@
     @FlakyTest(bugId = 240916028)
     @Test
     override fun appLayerReplacesLauncher() {
-        super.appLayerReplacesLauncher()
+        flicker.replacesLayer(
+            ComponentNameMatcher.LAUNCHER,
+            testApp,
+            ignoreEntriesWithRotationLayer = true,
+            ignoreSnapshot = true,
+            ignoreSplashscreen = false
+        )
     }
 
     @FlakyTest(bugId = 240916028)